aspera-cli 4.2.1 → 4.2.2

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.
@@ -16,33 +16,47 @@ module Aspera
16
16
  FILE_LIST_FROM_ARGS='@args'
17
17
  # special value for --sources : read file list from transfer spec (--ts)
18
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={"create_dir"=>true}
19
+ DEFAULT_TRANSFER_NOTIF_TMPL=<<END_OF_TEMPLATE
20
+ From: <%=from_name%> <<%=from_email%>>
21
+ To: <<%=to%>>
22
+ Subject: <%=subject%>
23
+
24
+ Transfer is: <%=global_transfer_status%>
25
+
26
+ <%=ts.to_yaml%>
27
+ END_OF_TEMPLATE
28
+ #%
29
+ private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:DEFAULT_TRANSFER_NOTIF_TMPL
30
+ # @param env external objects: option manager, config file manager
31
+ def initialize(env)
32
+ # same as plugin environment
33
+ @env=env
34
+ # command line can override transfer spec
35
+ @transfer_spec_cmdline={'create_dir'=>true}
26
36
  # the currently selected transfer agent
27
37
  @agent=nil
28
38
  @progress_listener=Listener::ProgressMulti.new
29
39
  # source/destination pair, like "paths" of transfer spec
30
40
  @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!
41
+ options.set_obj_attr(:ts,self,:option_transfer_spec)
42
+ options.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{options.get_option(:ts,:optional)}")
43
+ options.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{options.get_option(:local_resume,:optional)}")
44
+ options.add_opt_simple(:to_folder,"destination folder for downloaded files")
45
+ options.add_opt_simple(:sources,"list of source files (see doc)")
46
+ options.add_opt_simple(:transfer_info,"additional information for transfer client")
47
+ options.add_opt_list(:src_type,[:list,:pair],"type of file list")
48
+ options.add_opt_list(:transfer,[:direct,:httpgw,:connect,:node,:aoc],"type of transfer")
49
+ options.add_opt_list(:progress,[:none,:native,:multi],"type of progress bar")
50
+ options.set_option(:transfer,:direct)
51
+ options.set_option(:src_type,:list)
52
+ options.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
53
+ options.parse_options!
44
54
  end
45
55
 
56
+ def options; @env[:options];end
57
+
58
+ def config; @env[:config];end
59
+
46
60
  def option_transfer_spec; @transfer_spec_cmdline; end
47
61
 
48
62
  # multiple option are merged
@@ -54,8 +68,8 @@ module Aspera
54
68
  @agent=instance
55
69
  @agent.add_listener(Listener::Logger.new)
56
70
  # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
57
- if @opt_mgr.get_option(:progress,:mandatory).eql?(:multi) or
58
- (@opt_mgr.get_option(:progress,:mandatory).eql?(:native) and !@opt_mgr.get_option(:transfer,:mandatory).eql?(:direct))
71
+ if options.get_option(:progress,:mandatory).eql?(:multi) or
72
+ (options.get_option(:progress,:mandatory).eql?(:native) and !options.get_option(:transfer,:mandatory).eql?(:direct))
59
73
  @agent.add_listener(@progress_listener)
60
74
  end
61
75
  end
@@ -63,15 +77,15 @@ module Aspera
63
77
  # analyze options and create new agent if not already created or set
64
78
  def set_agent_by_options
65
79
  return nil unless @agent.nil?
66
- agent_type=@opt_mgr.get_option(:transfer,:mandatory)
80
+ agent_type=options.get_option(:transfer,:mandatory)
67
81
  case agent_type
68
82
  when :direct
69
- agent_options=@opt_mgr.get_option(:transfer_info,:optional)
83
+ agent_options=options.get_option(:transfer_info,:optional)
70
84
  agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
71
85
  new_agent=Fasp::Local.new(agent_options)
72
- new_agent.quiet=false if @opt_mgr.get_option(:progress,:mandatory).eql?(:native)
86
+ new_agent.quiet=false if options.get_option(:progress,:mandatory).eql?(:native)
73
87
  when :httpgw
74
- httpgw_config=@opt_mgr.get_option(:transfer_info,:mandatory)
88
+ httpgw_config=options.get_option(:transfer_info,:mandatory)
75
89
  new_agent=Fasp::HttpGW.new(httpgw_config)
76
90
  when :connect
77
91
  new_agent=Fasp::Connect.new
@@ -79,12 +93,12 @@ module Aspera
79
93
  # way for code to setup alternate node api in advance
80
94
  # support: @preset:<name>
81
95
  # support extended values
82
- node_config=@opt_mgr.get_option(:transfer_info,:optional)
96
+ node_config=options.get_option(:transfer_info,:optional)
83
97
  # if not specified: use default node
84
98
  if node_config.nil?
85
- param_set_name=@config.get_plugin_default_config_name(:node)
99
+ param_set_name=config.get_plugin_default_config_name(:node)
86
100
  raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
87
- node_config=@config.preset_by_name(param_set_name)
101
+ node_config=config.preset_by_name(param_set_name)
88
102
  end
89
103
  Log.log.debug("node=#{node_config}")
90
104
  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)
@@ -103,11 +117,11 @@ module Aspera
103
117
  }})
104
118
  new_agent=Fasp::Node.new(node_api)
105
119
  when :aoc
106
- aoc_config=@opt_mgr.get_option(:transfer_info,:optional)
120
+ aoc_config=options.get_option(:transfer_info,:optional)
107
121
  if aoc_config.nil?
108
- param_set_name=@config.get_plugin_default_config_name(:aspera)
122
+ param_set_name=config.get_plugin_default_config_name(:aspera)
109
123
  raise CliBadArgument,"No default AoC configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
110
- aoc_config=@config.preset_by_name(param_set_name)
124
+ aoc_config=config.preset_by_name(param_set_name)
111
125
  end
112
126
  Log.log.debug("aoc=#{aoc_config}")
113
127
  raise CliBadArgument,"the aoc configuration shall be Hash, not #{aoc_config.class} (#{aoc_config}), refer to manual" if !aoc_config.is_a?(Hash)
@@ -129,7 +143,7 @@ module Aspera
129
143
  # sets default if needed
130
144
  # param: 'send' or 'receive'
131
145
  def destination_folder(direction)
132
- dest_folder=@opt_mgr.get_option(:to_folder,:optional)
146
+ dest_folder=options.get_option(:to_folder,:optional)
133
147
  return dest_folder unless dest_folder.nil?
134
148
  dest_folder=@transfer_spec_cmdline['destination_root']
135
149
  return dest_folder unless dest_folder.nil?
@@ -152,12 +166,12 @@ module Aspera
152
166
  # start with lower priority : get paths from transfer spec on command line
153
167
  @transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
154
168
  # is there a source list option ?
155
- file_list=@opt_mgr.get_option(:sources,:optional)
169
+ file_list=options.get_option(:sources,:optional)
156
170
  case file_list
157
171
  when nil,FILE_LIST_FROM_ARGS
158
172
  Log.log.debug("getting file list as parameters")
159
173
  # get remaining arguments
160
- file_list=@opt_mgr.get_next_argument("source file list",:multiple)
174
+ file_list=options.get_next_argument("source file list",:multiple)
161
175
  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?
162
176
  when FILE_LIST_FROM_TRANSFER_SPEC
163
177
  Log.log.debug("assume list provided in transfer spec")
@@ -174,7 +188,7 @@ module Aspera
174
188
  if !@transfer_paths.nil?
175
189
  Log.log.warn("--sources overrides paths from --ts")
176
190
  end
177
- case @opt_mgr.get_option(:src_type,:mandatory)
191
+ case options.get_option(:src_type,:mandatory)
178
192
  when :list
179
193
  # when providing a list, just specify source
180
194
  @transfer_paths=file_list.map{|i|{'source'=>i}}
@@ -189,20 +203,20 @@ module Aspera
189
203
 
190
204
  # start a transfer and wait for completion, plugins shall use this method
191
205
  # @param transfer_spec
192
- # @param options specific options for the transfer_agent
193
- # options[:src] specifies how destination_root is set (how transfer spec was generated)
206
+ # @param tr_opts specific options for the transfer_agent
207
+ # tr_opts[:src] specifies how destination_root is set (how transfer spec was generated)
194
208
  # other options are carried to specific agent
195
- def start(transfer_spec,options)
209
+ def start(transfer_spec,tr_opts)
196
210
  # check parameters
197
211
  raise "transfer_spec must be hash" unless transfer_spec.is_a?(Hash)
198
- raise "options must be hash" unless options.is_a?(Hash)
212
+ raise "tr_opts must be hash" unless tr_opts.is_a?(Hash)
199
213
  # process :src option
200
214
  case transfer_spec['direction']
201
215
  when 'receive'
202
216
  # init default if required in any case
203
217
  @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
204
218
  when 'send'
205
- case options[:src]
219
+ case tr_opts[:src]
206
220
  when :direct
207
221
  # init default if required
208
222
  @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
@@ -213,12 +227,12 @@ module Aspera
213
227
  when :node_gen4
214
228
  @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
215
229
  else
216
- raise StandardError,"InternalError: unsupported value: #{options[:src]}"
230
+ raise StandardError,"InternalError: unsupported value: #{tr_opts[:src]}"
217
231
  end
218
232
  end
219
233
 
220
234
  # only used here
221
- options.delete(:src)
235
+ tr_opts.delete(:src)
222
236
 
223
237
  # update command line paths, unless destination already has one
224
238
  @transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
@@ -227,13 +241,26 @@ module Aspera
227
241
  # create transfer agent
228
242
  self.set_agent_by_options
229
243
  Log.log.debug("transfer agent is a #{@agent.class}")
230
- @agent.start_transfer(transfer_spec,options)
244
+ @agent.start_transfer(transfer_spec,tr_opts)
231
245
  result=@agent.wait_for_transfers_completion
232
246
  @progress_listener.reset
233
247
  Fasp::Manager.validate_status_list(result)
248
+ send_email_transfer_notification(transfer_spec,result)
234
249
  return result
235
250
  end
236
251
 
252
+ def send_email_transfer_notification(transfer_spec,statuses)
253
+ return if options.get_option(:notif_to,:optional).nil?
254
+ global_status=self.class.session_status(statuses)
255
+ email_vars={
256
+ global_transfer_status: global_status,
257
+ subject: "ascli transfer: #{global_status}",
258
+ body: "Transfer is: #{global_status}",
259
+ ts: transfer_spec
260
+ }
261
+ @env[:config].send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
262
+ end
263
+
237
264
  # @return :success if all sessions statuses returned by "start" are success
238
265
  # else return the first error exception object
239
266
  def self.session_status(statuses)
@@ -1,5 +1,5 @@
1
1
  module Aspera
2
2
  module Cli
3
- VERSION = "4.2.1"
3
+ VERSION = "4.2.2"
4
4
  end
5
5
  end
@@ -4,21 +4,46 @@ module Aspera
4
4
  # process_param is called repeatedly with all known parameters
5
5
  # add_env_args is called to get resulting param list and env var (also checks that all params were used)
6
6
  class CommandLineBuilder
7
+ # transform yes/no to trye/false
8
+ def self.yes_to_true(value)
9
+ case value
10
+ when 'yes'; return true
11
+ when 'no'; return false
12
+ end
13
+ raise "unsupported value: #{value}"
14
+ end
7
15
 
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('_','-')
16
+ # Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
17
+ def self.normalize_description(d)
18
+ d.each do |param_name,options|
19
+ Log.log.debug("def: #{param_name}")
20
+ raise "Expecting Hash, but have #{options.class} in #{param_name}" unless options.is_a?(Hash)
21
+ #options[:accepted_types]=:bool if options[:cltype].eql?(:envvar) and !options.has_key?(:accepted_types)
22
+ # by default : not mandatory
23
+ options[:mandatory]||=false
24
+ options[:desc]||=''
25
+ # by default : string, unless it's without arg
26
+ if ! options.has_key?(:accepted_types)
27
+ options[:accepted_types]=options[:cltype].eql?(:opt_without_arg) ? :bool : :string
28
+ end
29
+ # single type is placed in array
30
+ options[:accepted_types]=[options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
31
+ if !options.has_key?(:option_switch) and options.has_key?(:cltype) and [:opt_without_arg,:opt_with_arg].include?(options[:cltype])
32
+ options[:option_switch]='--'+param_name.to_s.gsub('_','-')
33
+ end
34
+ end
13
35
  end
14
36
 
37
+ private
38
+
39
+ # clvarname : command line variable name
15
40
  def env_name(param_name,options)
16
- return options[:variable]
41
+ return options[:clvarname]
17
42
  end
18
43
 
19
44
  public
20
45
 
21
- BOOLEAN_CLASSES=[TrueClass,FalseClass]
46
+ attr_reader :params_definition
22
47
 
23
48
  # @param param_hash
24
49
  def initialize(param_hash,params_definition)
@@ -44,15 +69,6 @@ module Aspera
44
69
  return nil
45
70
  end
46
71
 
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
72
  # add options directly to ascp command line
57
73
  def add_command_line_options(options)
58
74
  return if options.nil?
@@ -71,24 +87,25 @@ module Aspera
71
87
  # @param options : options for type
72
88
  def process_param(param_name,action=nil)
73
89
  options=@params_definition[param_name]
74
- action=options[:type] if action.nil?
90
+ action=options[:cltype] if action.nil?
75
91
  # should not happen
76
92
  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
93
  # check mandatory parameter (nil is valid value)
87
94
  raise Fasp::Error.new("mandatory parameter: #{param_name}") if options[:mandatory] and !@param_hash.has_key?(param_name)
88
95
  parameter_value=@param_hash[param_name]
89
- parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
96
+ #parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
97
+ expected_classes=options[:accepted_types].map do |s|
98
+ case s
99
+ when :string; String
100
+ when :array; Array
101
+ when :hash; Hash
102
+ when :int; Integer
103
+ when :bool; [TrueClass,FalseClass]
104
+ else raise "INTERNAL: unexpected value: #{s}"
105
+ end
106
+ end.flatten
90
107
  # 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)}
108
+ raise Fasp::Error.new("#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, ") unless parameter_value.nil? or expected_classes.include?(parameter_value.class)
92
109
  @used_param_names.push(param_name) unless action.eql?(:defer)
93
110
 
94
111
  # process only non-nil values
@@ -102,7 +119,8 @@ module Aspera
102
119
  end
103
120
  raise "unsupported value: #{parameter_value}" unless options[:accepted_values].nil? or options[:accepted_values].include?(parameter_value)
104
121
  if options[:encode]
105
- newvalue=options[:encode].call(parameter_value)
122
+ # :encode has name of class with encoding method
123
+ newvalue=Kernel.const_get(options[:encode]).send("encode_#{param_name}",parameter_value)
106
124
  raise Fasp::Error.new("unsupported #{param_name}: #{parameter_value}") if newvalue.nil?
107
125
  parameter_value=newvalue
108
126
  end
@@ -123,12 +141,12 @@ module Aspera
123
141
  else raise Fasp::Error.new("unsupported #{param_name}: #{parameter_value}")
124
142
  end
125
143
  add_param=!add_param if options[:add_on_false]
126
- add_command_line_options([switch_name(param_name,options)]) if add_param
144
+ add_command_line_options([options[:option_switch]]) if add_param
127
145
  when :opt_with_arg # transform into command line option with value
128
146
  #parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
129
147
  parameter_value=[parameter_value] unless parameter_value.is_a?(Array)
130
148
  # 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])}
149
+ parameter_value.each{|v|add_command_line_options([options[:option_switch],v])}
132
150
  else
133
151
  raise "Error"
134
152
  end
@@ -4,6 +4,7 @@ require 'aspera/temp_file_manager'
4
4
  require 'securerandom'
5
5
  require 'base64'
6
6
  require 'json'
7
+ require 'yaml'
7
8
  require 'securerandom'
8
9
  require 'fileutils'
9
10
 
@@ -12,97 +13,67 @@ module Aspera
12
13
  # translate transfer specification to ascp parameter list
13
14
  class Parameters
14
15
  private
15
- # temp folder for file lists, must contain only file lists
16
+ # Temp folder for file lists, must contain only file lists
16
17
  # because of garbage collection takes any file there
17
18
  # this could be refined, as , for instance, on macos, temp folder is already user specific
18
19
  @@file_list_folder=TempFileManager.instance.new_file_path_global('asession_filelists')
19
- PARAM_DEFINITION={
20
- # parameters with env vars
21
- 'remote_password' => { :type => :envvar, :variable=>'ASPERA_SCP_PASS'},
22
- 'token' => { :type => :envvar, :variable=>'ASPERA_SCP_TOKEN'},
23
- 'cookie' => { :type => :envvar, :variable=>'ASPERA_SCP_COOKIE'},
24
- 'ssh_private_key' => { :type => :envvar, :variable=>'ASPERA_SCP_KEY'},
25
- 'EX_at_rest_password' => { :type => :envvar, :variable=>'ASPERA_SCP_FILEPASS'},
26
- 'EX_proxy_password' => { :type => :envvar, :variable=>'ASPERA_PROXY_PASS'},
27
- 'EX_license_text' => { :type => :envvar, :variable=>'ASPERA_SCP_LICENSE'},
28
- # bool params
29
- 'create_dir' => { :type => :opt_without_arg, :option_switch=>'-d'},
30
- 'precalculate_job_size' => { :type => :opt_without_arg},
31
- 'keepalive' => { :type => :opt_without_arg},
32
- 'delete_before_transfer' => { :type => :opt_without_arg}, #TODO: doc readme
33
- 'preserve_access_time' => { :type => :opt_without_arg}, #TODO: doc
34
- 'preserve_creation_time' => { :type => :opt_without_arg}, #TODO: doc
35
- 'preserve_times' => { :type => :opt_without_arg}, #TODO: doc
36
- 'preserve_modification_time'=> { :type => :opt_without_arg}, #TODO: doc
37
- 'remove_empty_directories'=> { :type => :opt_without_arg}, #TODO: doc
38
- 'remove_after_transfer' => { :type => :opt_without_arg}, #TODO: doc
39
- 'remove_empty_source_directory'=> { :type => :opt_without_arg}, #TODO: doc
40
- # value params
41
- 'cipher' => { :type => :opt_with_arg, :option_switch=>'-c',:accepted_types=>String,:encode=>lambda{|cipher|cipher.tr('-','')}},
42
- 'resume_policy' => { :type => :opt_with_arg, :option_switch=>'-k',:accepted_types=>String,:default=>'sparse_csum',:translate_values=>{'none'=>0,'attrs'=>1,'sparse_csum'=>2,'full_csum'=>3}},
43
- 'direction' => { :type => :opt_with_arg, :option_switch=>'--mode',:accepted_types=>String,:translate_values=>{'receive'=>'recv','send'=>'send'}},
44
- 'remote_user' => { :type => :opt_with_arg, :option_switch=>'--user',:accepted_types=>String},
45
- 'remote_host' => { :type => :opt_with_arg, :option_switch=>'--host',:accepted_types=>String},
46
- 'ssh_port' => { :type => :opt_with_arg, :option_switch=>'-P',:accepted_types=>Integer},
47
- 'fasp_port' => { :type => :opt_with_arg, :option_switch=>'-O',:accepted_types=>Integer},
48
- 'dgram_size' => { :type => :opt_with_arg, :option_switch=>'-Z',:accepted_types=>Integer},
49
- 'target_rate_kbps' => { :type => :opt_with_arg, :option_switch=>'-l',:accepted_types=>Integer},
50
- 'min_rate_kbps' => { :type => :opt_with_arg, :option_switch=>'-m',:accepted_types=>Integer},
51
- 'rate_policy' => { :type => :opt_with_arg, :option_switch=>'--policy',:accepted_types=>String},
52
- 'http_fallback' => { :type => :opt_with_arg, :option_switch=>'-y',:accepted_types=>[String,*Aspera::CommandLineBuilder::BOOLEAN_CLASSES],:translate_values=>{'force'=>'F',true=>1,false=>0}},
53
- 'http_fallback_port' => { :type => :opt_with_arg, :option_switch=>'-t',:accepted_types=>Integer},
54
- 'source_root' => { :type => :opt_with_arg, :option_switch=>'--source-prefix64',:accepted_types=>String,:encode=>lambda{|prefix|Base64.strict_encode64(prefix)}},
55
- 'sshfp' => { :type => :opt_with_arg, :option_switch=>'--check-sshfp',:accepted_types=>String},
56
- 'symlink_policy' => { :type => :opt_with_arg, :option_switch=>'--symbolic-links',:accepted_types=>String},
57
- 'overwrite' => { :type => :opt_with_arg, :accepted_types=>String},
58
- 'exclude_newer_than' => { :type => :opt_with_arg, :accepted_types=>Integer},
59
- 'exclude_older_than' => { :type => :opt_with_arg, :accepted_types=>Integer},
60
- 'preserve_acls' => { :type => :opt_with_arg, :accepted_types=>String},
61
- 'move_after_transfer' => { :type => :opt_with_arg, :accepted_types=>String},
62
- 'multi_session_threshold' => { :type => :opt_with_arg, :accepted_types=>Integer},
63
- # non standard parameters
64
- 'EX_fasp_proxy_url' => { :type => :opt_with_arg, :option_switch=>'--proxy',:accepted_types=>String},
65
- 'EX_http_proxy_url' => { :type => :opt_with_arg, :option_switch=>'-x',:accepted_types=>String},
66
- 'EX_ssh_key_paths' => { :type => :opt_with_arg, :option_switch=>'-i',:accepted_types=>Array},
67
- 'EX_http_transfer_jpeg' => { :type => :opt_with_arg, :option_switch=>'-j',:accepted_types=>Integer},
68
- 'EX_multi_session_part' => { :type => :opt_with_arg, :option_switch=>'-C',:accepted_types=>String},
69
- 'EX_no_read' => { :type => :opt_without_arg, :option_switch=>'--no-read'},
70
- 'EX_no_write' => { :type => :opt_without_arg, :option_switch=>'--no-write'},
71
- 'EX_apply_local_docroot' => { :type => :opt_without_arg, :option_switch=>'--apply-local-docroot'},
72
- # TODO: manage those parameters, some are for connect only ? node api ?
73
- 'target_rate_cap_kbps' => { :type => :ignore, :accepted_types=>Integer},
74
- 'target_rate_percentage' => { :type => :ignore, :accepted_types=>String}, # -wf -l<rate>p
75
- 'min_rate_cap_kbps' => { :type => :ignore, :accepted_types=>Integer},
76
- 'rate_policy_allowed' => { :type => :ignore, :accepted_types=>String},
77
- 'fasp_url' => { :type => :ignore, :accepted_types=>String},
78
- 'lock_rate_policy' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
79
- 'lock_min_rate' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
80
- 'lock_target_rate' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
81
- 'authentication' => { :type => :ignore, :accepted_types=>String}, # value = token
82
- 'https_fallback_port' => { :type => :ignore, :accepted_types=>Integer}, # same as http fallback, option -t ?
83
- 'content_protection' => { :type => :ignore, :accepted_types=>String},
84
- 'cipher_allowed' => { :type => :ignore, :accepted_types=>String},
85
- 'multi_session' => { :type => :ignore, :accepted_types=>Integer}, # managed
86
- 'obfuscate_file_names' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
87
- # optional tags ( additional option to generate: {:space=>' ',:object_nl=>' ',:space_before=>'+',:array_nl=>'1'} )
88
- 'tags' => { :type => :opt_with_arg, :option_switch=>'--tags64',:accepted_types=>Hash,:encode=>lambda{|tags|Base64.strict_encode64(JSON.generate(tags))}},
89
- # special processing @builder.process_param( called individually
90
- 'use_ascp4' => { :type => :defer, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
91
- 'paths' => { :type => :defer, :accepted_types=>Array},
92
- 'EX_file_list' => { :type => :defer, :option_switch=>'--file-list', :accepted_types=>String},
93
- 'EX_file_pair_list' => { :type => :defer, :option_switch=>'--file-pair-list', :accepted_types=>String},
94
- 'EX_ascp_args' => { :type => :defer, :accepted_types=>Array},
95
- 'destination_root' => { :type => :defer, :accepted_types=>String},
96
- 'wss_enabled' => { :type => :defer, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
97
- 'wss_port' => { :type => :defer, :accepted_types=>Integer},
98
- }
99
-
100
- private_constant :PARAM_DEFINITION
20
+ @@param_description_cache=nil
21
+ # @return normaiwed description of transfer spec parameters
22
+ def self.description
23
+ return @@param_description_cache unless @@param_description_cache.nil?
24
+ # config file in same folder with same name as this source
25
+ @@param_description_cache=YAML.load_file("#{__FILE__[0..-3]}yaml")
26
+ Aspera::CommandLineBuilder.normalize_description(@@param_description_cache)
27
+ end
28
+
29
+ # Agents shown in manual
30
+ SUPPORTED_AGENTS=[:direct,:node,:connect]
31
+ # Short names of columns in manual
32
+ SUPPORTED_AGENTS_SHORT=SUPPORTED_AGENTS.map{|a|a.to_s[0].to_sym}
33
+
34
+ # @return a table suitable to display a manual
35
+ def self.man_table
36
+ result=[]
37
+ description.keys.map do |k|
38
+ i=description[k]
39
+ param={name: k, type: [i[:accepted_types]].flatten.join(','),description: i[:desc]}
40
+ SUPPORTED_AGENTS.each do |a|
41
+ param[a.to_s[0].to_sym]=i[:context].nil? || i[:context].include?(a) ? 'Y' : ''
42
+ end
43
+ # only keep lines that are usable in supported agents
44
+ next if SUPPORTED_AGENTS_SHORT.inject(true){|m,i|m and param[i].empty?}
45
+ param[:cli]=case i[:cltype]
46
+ when :envvar; 'env:'+i[:clvarname]
47
+ when :opt_without_arg,:opt_with_arg; i[:option_switch]
48
+ else ''
49
+ end
50
+ if i.has_key?(:enum)
51
+ param[:description] << "\nAllowed values: #{i[:enum].join(', ')}"
52
+ end
53
+ result.push(param)
54
+ end
55
+ return result
56
+ end
57
+
58
+ # special encoding methods used in YAML (key: :encode)
59
+ def self.encode_cipher(v)
60
+ v.tr('-','')
61
+ end
62
+
63
+ # special encoding methods used in YAML (key: :encode)
64
+ def self.encode_source_root(v)
65
+ Base64.strict_encode64(v)
66
+ end
67
+
68
+ # special encoding methods used in YAML (key: :encode)
69
+ def self.encode_tags(v)
70
+ Base64.strict_encode64(JSON.generate(v))
71
+ end
101
72
 
102
73
  def initialize(job_spec,options)
103
74
  @job_spec=job_spec
104
- @builder=Aspera::CommandLineBuilder.new(@job_spec,PARAM_DEFINITION)
105
75
  @options=options
76
+ @builder=Aspera::CommandLineBuilder.new(@job_spec,self.class.description)
106
77
  end
107
78
 
108
79
  public
@@ -125,7 +96,7 @@ module Aspera
125
96
  # special cases
126
97
  @job_spec.delete('source_root') if @job_spec.has_key?('source_root') and @job_spec['source_root'].empty?
127
98
 
128
- # use web socket initiation ?
99
+ # use web socket session initiation ?
129
100
  if @builder.process_param('wss_enabled',:get_value) and @options[:wss]
130
101
  # by default use web socket session if available, unless removed by user
131
102
  @builder.add_command_line_options(['--ws-connect'])
@@ -151,8 +122,7 @@ module Aspera
151
122
  # destination will be base64 encoded, put before path arguments
152
123
  @builder.add_command_line_options(['--dest64'])
153
124
  end
154
-
155
- PARAM_DEFINITION['paths'][:mandatory]=!@job_spec.has_key?('keepalive')
125
+ @builder.params_definition['paths'][:mandatory]=!@job_spec.has_key?('keepalive')
156
126
  paths_array=@builder.process_param('paths',:get_value)
157
127
  unless paths_array.nil?
158
128
  # use file list if there is storage defined for it.