aspera-cli 4.0.0 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +843 -304
  3. data/bin/dascli +13 -0
  4. data/docs/Makefile +4 -4
  5. data/docs/README.erb.md +805 -172
  6. data/docs/test_env.conf +22 -3
  7. data/examples/aoc.rb +14 -3
  8. data/examples/faspex4.rb +89 -0
  9. data/lib/aspera/aoc.rb +87 -108
  10. data/lib/aspera/cli/formater.rb +2 -0
  11. data/lib/aspera/cli/main.rb +89 -49
  12. data/lib/aspera/cli/plugin.rb +9 -4
  13. data/lib/aspera/cli/plugins/alee.rb +1 -1
  14. data/lib/aspera/cli/plugins/aoc.rb +188 -173
  15. data/lib/aspera/cli/plugins/ats.rb +2 -2
  16. data/lib/aspera/cli/plugins/config.rb +218 -145
  17. data/lib/aspera/cli/plugins/console.rb +2 -2
  18. data/lib/aspera/cli/plugins/faspex.rb +114 -61
  19. data/lib/aspera/cli/plugins/faspex5.rb +85 -43
  20. data/lib/aspera/cli/plugins/node.rb +3 -3
  21. data/lib/aspera/cli/plugins/preview.rb +59 -45
  22. data/lib/aspera/cli/plugins/server.rb +23 -8
  23. data/lib/aspera/cli/transfer_agent.rb +77 -49
  24. data/lib/aspera/cli/version.rb +1 -1
  25. data/lib/aspera/command_line_builder.rb +49 -31
  26. data/lib/aspera/cos_node.rb +33 -28
  27. data/lib/aspera/environment.rb +2 -2
  28. data/lib/aspera/fasp/connect.rb +28 -21
  29. data/lib/aspera/fasp/http_gw.rb +140 -28
  30. data/lib/aspera/fasp/installation.rb +93 -46
  31. data/lib/aspera/fasp/local.rb +88 -45
  32. data/lib/aspera/fasp/manager.rb +15 -0
  33. data/lib/aspera/fasp/node.rb +4 -4
  34. data/lib/aspera/fasp/parameters.rb +59 -101
  35. data/lib/aspera/fasp/parameters.yaml +531 -0
  36. data/lib/aspera/fasp/resume_policy.rb +13 -12
  37. data/lib/aspera/fasp/uri.rb +1 -1
  38. data/lib/aspera/log.rb +1 -1
  39. data/lib/aspera/node.rb +61 -1
  40. data/lib/aspera/oauth.rb +49 -46
  41. data/lib/aspera/persistency_folder.rb +9 -4
  42. data/lib/aspera/preview/file_types.rb +53 -21
  43. data/lib/aspera/preview/generator.rb +3 -3
  44. data/lib/aspera/rest.rb +29 -18
  45. data/lib/aspera/secrets.rb +20 -0
  46. data/lib/aspera/sync.rb +40 -35
  47. data/lib/aspera/temp_file_manager.rb +19 -0
  48. data/lib/aspera/web_auth.rb +105 -0
  49. metadata +54 -20
  50. data/docs/transfer_spec.html +0 -99
@@ -10,6 +10,7 @@ require 'aspera/persistency_folder'
10
10
  require 'aspera/log'
11
11
  require 'aspera/rest'
12
12
  require 'aspera/nagios'
13
+ require 'aspera/secrets'
13
14
 
14
15
  module Aspera
15
16
  module Cli
@@ -20,15 +21,17 @@ module Aspera
20
21
  private
21
22
  # name of application, also foldername where config is stored
22
23
  PROGRAM_NAME = 'ascli'
24
+ # name of the containing gem, same as in <gem name>.gemspec
23
25
  GEM_NAME = 'aspera-cli'
24
- VERBOSE_LEVELS=[:normal,:minimal,:quiet]
25
-
26
- private_constant :PROGRAM_NAME,:GEM_NAME,:VERBOSE_LEVELS
26
+ HELP_URL = "http://www.rubydoc.info/gems/#{GEM_NAME}"
27
+ GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
28
+ SRC_URL = "https://github.com/IBM/aspera-cli"
29
+ STATUS_FIELD = 'status'
27
30
 
31
+ private_constant :PROGRAM_NAME,:GEM_NAME,:HELP_URL,:GEM_URL
28
32
  # =============================================================
29
33
  # Parameter handlers
30
34
  #
31
-
32
35
  def option_insecure; Rest.insecure ; end
33
36
 
34
37
  def option_insecure=(value); Rest.insecure = value; end
@@ -39,32 +42,41 @@ module Aspera
39
42
 
40
43
  # minimum initialization
41
44
  def initialize(argv)
42
- # first thing : manage debug level (allows debugging or option parser)
45
+ # first thing : manage debug level (allows debugging of option parser)
43
46
  early_debug_setup(argv)
47
+ # compare $0 with expected name
44
48
  current_prog_name=File.basename($PROGRAM_NAME)
45
49
  unless current_prog_name.eql?(PROGRAM_NAME)
46
50
  @plugin_env[:formater].display_message(:error,"#{"WARNING".bg_red.blink.gray} Please use '#{PROGRAM_NAME}' instead of '#{current_prog_name}', '#{current_prog_name}' will be removed in a future version")
47
51
  end
48
- # overriding parameters on transfer spec
49
52
  @option_help=false
50
53
  @bash_completion=false
51
54
  @option_show_config=false
55
+ # environment provided to plugin for various capabilities
52
56
  @plugin_env={}
53
- @help_url='http://www.rubydoc.info/gems/'+GEM_NAME
54
- @gem_url='https://rubygems.org/gems/'+GEM_NAME
57
+ # find out application main folder
58
+ app_main_folder=ENV[conf_dir_env_var]
59
+ # if env var undefined or empty
60
+ if app_main_folder.nil? or app_main_folder.empty?
61
+ user_home_folder=Dir.home
62
+ raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
63
+ app_main_folder=File.join(user_home_folder,Plugins::Config::ASPERA_HOME_FOLDER_NAME,PROGRAM_NAME)
64
+ end
55
65
  # give command line arguments to option manager (no parsing)
56
- @plugin_env[:options]=@opt_mgr=Manager.new(self.program_name,argv,app_banner())
66
+ @plugin_env[:options]=@opt_mgr=Manager.new(PROGRAM_NAME,argv,app_banner())
57
67
  @plugin_env[:formater]=Formater.new(@plugin_env[:options])
58
- Rest.user_agent=self.program_name
59
- # must override help methods before parser called (in other constructors)
68
+ Rest.user_agent=PROGRAM_NAME
69
+ # declare and parse global options
60
70
  init_global_options()
71
+ # secret manager
72
+ @plugin_env[:secret]=Aspera::Secrets.new
61
73
  # the Config plugin adds the @preset parser
62
- @plugin_env[:config]=Plugins::Config.new(@plugin_env,self.program_name,@help_url,Aspera::Cli::VERSION)
74
+ @plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,HELP_URL,Aspera::Cli::VERSION,app_main_folder)
63
75
  # the TransferAgent plugin may use the @preset parser
64
76
  @plugin_env[:transfer]=TransferAgent.new(@plugin_env)
65
- Log.log.debug('created plugin env'.red)
66
77
  # set application folder for modules
67
78
  @plugin_env[:persistency]=PersistencyFolder.new(File.join(@plugin_env[:config].main_folder,'persist_store'))
79
+ Log.log.debug('plugin env created'.red)
68
80
  Oauth.persist_mgr=@plugin_env[:persistency]
69
81
  Fasp::Parameters.file_list_folder=File.join(@plugin_env[:config].main_folder,'filelists')
70
82
  Aspera::RestErrorAnalyzer.instance.log_file=File.join(@plugin_env[:config].main_folder,'rest_exceptions.log')
@@ -73,21 +85,21 @@ module Aspera
73
85
  end
74
86
 
75
87
  def app_banner
76
- banner = "NAME\n\t#{self.program_name} -- a command line tool for Aspera Applications (v#{Aspera::Cli::VERSION})\n\n"
88
+ banner = "NAME\n\t#{PROGRAM_NAME} -- a command line tool for Aspera Applications (v#{Aspera::Cli::VERSION})\n\n"
77
89
  banner << "SYNOPSIS\n"
78
- banner << "\t#{self.program_name} COMMANDS [OPTIONS] [ARGS]\n"
79
- banner << "\n"
80
- banner << "DESCRIPTION\n"
90
+ banner << "\t#{PROGRAM_NAME} COMMANDS [OPTIONS] [ARGS]\n"
91
+ banner << "\nDESCRIPTION\n"
81
92
  banner << "\tUse Aspera application to perform operations on command line.\n"
82
- banner << "\tDocumentation and examples: #{@gem_url}\n"
83
- banner << "\texecute: #{self.program_name} conf doc\n"
84
- banner << "\tor visit: #{@help_url}\n"
85
- banner << "\n"
86
- banner << "COMMANDS\n"
87
- banner << "\tTo list first level commands, execute: #{self.program_name}\n"
93
+ banner << "\tDocumentation and examples: #{GEM_URL}\n"
94
+ banner << "\texecute: #{PROGRAM_NAME} conf doc\n"
95
+ banner << "\tor visit: #{HELP_URL}\n"
96
+ banner << "\nENVIRONMENT VARIABLES\n"
97
+ banner << "\t#{conf_dir_env_var} config folder, default: $HOME/#{Plugins::Config::ASPERA_HOME_FOLDER_NAME}/#{PROGRAM_NAME}\n"
98
+ banner << "\t#any option can be set as an environment variable, refer to the manual\n"
99
+ banner << "\nCOMMANDS\n"
100
+ banner << "\tTo list first level commands, execute: #{PROGRAM_NAME}\n"
88
101
  banner << "\tNote that commands can be written shortened (provided it is unique).\n"
89
- banner << "\n"
90
- banner << "OPTIONS\n"
102
+ banner << "\nOPTIONS\n"
91
103
  banner << "\tOptions begin with a '-' (minus), and value is provided on command line.\n"
92
104
  banner << "\tSpecial values are supported beginning with special prefix, like: #{ExtendedValue.instance.modifiers.map{|m|"@#{m}:"}.join(' ')}.\n"
93
105
  banner << "\tDates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'\n\n"
@@ -167,7 +179,7 @@ module Aspera
167
179
  # override main option parser with a brand new, to avoid having global options
168
180
  plugin_env=@plugin_env.clone
169
181
  plugin_env[:man_only]=true
170
- plugin_env[:options]=Manager.new(self.program_name,[],'')
182
+ plugin_env[:options]=Manager.new(PROGRAM_NAME,[],'')
171
183
  get_plugin_instance_with_options(plugin_name_sym,plugin_env)
172
184
  # display generated help for plugin options
173
185
  @plugin_env[:formater].display_message(:error,plugin_env[:options].parser.to_s)
@@ -178,10 +190,16 @@ module Aspera
178
190
 
179
191
  protected
180
192
 
193
+ # env var name to override the app's main folder
194
+ # default main folder is $HOME/<vendor main app folder>/<program name>
195
+ def conf_dir_env_var
196
+ return "#{PROGRAM_NAME}_home".upcase
197
+ end
198
+
181
199
  # early debug for parser
182
200
  # Note: does not accept shortcuts
183
201
  def early_debug_setup(argv)
184
- Log.instance.program_name=self.program_name
202
+ Log.instance.program_name=PROGRAM_NAME
185
203
  argv.each do |arg|
186
204
  case arg
187
205
  when '--'
@@ -205,14 +223,28 @@ module Aspera
205
223
  return Main.result_nothing
206
224
  end
207
225
 
208
- def options;@opt_mgr;end
209
-
210
- def program_name;PROGRAM_NAME;end
226
+ # used when one command executes several transfer jobs (each job being possibly multi session)
227
+ # @param status_table [Array] [{STATUS_FIELD=>[status array],...},...]
228
+ # each element has a key STATUS_FIELD which contains the result of possibly mulmtiple sessions
229
+ def self.result_transfer_multiple(status_table)
230
+ global_status=:success
231
+ # transform status into string and find if there was problem
232
+ status_table.each do |item|
233
+ worst=TransferAgent.session_status(item[STATUS_FIELD])
234
+ global_status=worst unless worst.eql?(:success)
235
+ item[STATUS_FIELD]=item[STATUS_FIELD].map{|i|i.to_s}.join(',')
236
+ end
237
+ raise global_status unless global_status.eql?(:success)
238
+ return {:type=>:object_list,:data=>status_table}
239
+ end
211
240
 
212
241
  # this is the main function called by initial script just after constructor
213
242
  def process_command_line
214
243
  Log.log.debug('process_command_line')
244
+ # catch exception information , if any
215
245
  exception_info=nil
246
+ # false if command shall not be executed ("once_only")
247
+ execute_command=true
216
248
  begin
217
249
  # find plugins, shall be after parse! ?
218
250
  @plugin_env[:config].add_plugins_from_lookup_folders
@@ -222,16 +254,7 @@ module Aspera
222
254
  # load global default options and process
223
255
  @plugin_env[:config].add_plugin_default_preset(Plugins::Config::CONF_GLOBAL_SYM)
224
256
  @opt_mgr.parse_options!
225
- # dual execution locking
226
- lock_port=@opt_mgr.get_option(:lock_port,:optional)
227
- if !lock_port.nil?
228
- begin
229
- # no need to close later, will be freed on process exit. must save in member else it is garbage collected
230
- @tcp_server=TCPServer.new('127.0.0.1',lock_port.to_i)
231
- rescue => e
232
- raise CliError,"Another instance is already running (lock port=#{lock_port})."
233
- end
234
- end
257
+ @plugin_env[:config].periodic_check_newer_gem_version
235
258
  if @option_show_config and @opt_mgr.command_or_arg_empty?
236
259
  command_sym=Plugins::Config::CONF_PLUGIN_SYM
237
260
  else
@@ -255,18 +278,30 @@ module Aspera
255
278
  @plugin_env[:formater].display_results({:type=>:single_object,:data=>@opt_mgr.declared_options(false)})
256
279
  Process.exit(0)
257
280
  end
258
- # execute and display
259
- @plugin_env[:formater].display_results(command_plugin.execute_action)
281
+ # locking for single execution (only after "per plugin" option, in case lock port is there)
282
+ lock_port=@opt_mgr.get_option(:lock_port,:optional)
283
+ if !lock_port.nil?
284
+ begin
285
+ # no need to close later, will be freed on process exit. must save in member else it is garbage collected
286
+ Log.log.debug("Opening lock port #{lock_port.to_i}")
287
+ @tcp_server=TCPServer.new('127.0.0.1',lock_port.to_i)
288
+ rescue => e
289
+ execute_command=false
290
+ Log.log.warn("Another instance is already running (#{e.message}).")
291
+ end
292
+ end
293
+ # execute and display (if not exclusive execution)
294
+ @plugin_env[:formater].display_results(command_plugin.execute_action) if execute_command
260
295
  # finish
261
296
  @plugin_env[:transfer].shutdown
262
297
  rescue CliBadArgument => e; exception_info=[e,'Argument',:usage]
263
298
  rescue CliNoSuchId => e; exception_info=[e,'Identifier']
264
299
  rescue CliError => e; exception_info=[e,'Tool',:usage]
265
- rescue Fasp::Error => e; exception_info=[e,"FASP(ascp]"]
266
- rescue Aspera::RestCallError => e; exception_info=[e,"Rest"]
267
- rescue SocketError => e; exception_info=[e,"Network"]
268
- rescue StandardError => e; exception_info=[e,"Other",:debug]
269
- rescue Interrupt => e; exception_info=[e,"Interruption",:debug]
300
+ rescue Fasp::Error => e; exception_info=[e,'FASP(ascp)']
301
+ rescue Aspera::RestCallError => e; exception_info=[e,'Rest']
302
+ rescue SocketError => e; exception_info=[e,'Network']
303
+ rescue StandardError => e; exception_info=[e,'Other',:debug]
304
+ rescue Interrupt => e; exception_info=[e,'Interruption',:debug]
270
305
  end
271
306
  # cleanup file list files
272
307
  TempFileManager.instance.cleanup
@@ -274,10 +309,15 @@ module Aspera
274
309
  unless exception_info.nil?
275
310
  @plugin_env[:formater].display_message(:error,"ERROR:".bg_red.gray.blink+" "+exception_info[1]+": "+exception_info[0].message)
276
311
  @plugin_env[:formater].display_message(:error,"Use '-h' option to get help.") if exception_info[2].eql?(:usage)
312
+ if exception_info.first.is_a?(Fasp::Error) and exception_info.first.message.eql?('Remote host is not who we expected')
313
+ @plugin_env[:formater].display_message(:error,"For this specific error, refer to:\n#{SRC_URL}#error-remote-host-is-not-who-we-expected\nAdd this to arguments:\n--ts=@json:'{\"sshfp\":null}'")
314
+ end
277
315
  end
278
316
  # 2- processing of command not processed (due to exception or bad command line)
279
- @opt_mgr.final_errors.each do |msg|
280
- @plugin_env[:formater].display_message(:error,"ERROR:".bg_red.gray.blink+" Argument: "+msg)
317
+ if execute_command
318
+ @opt_mgr.final_errors.each do |msg|
319
+ @plugin_env[:formater].display_message(:error,"ERROR:".bg_red.gray.blink+" Argument: "+msg)
320
+ end
281
321
  end
282
322
  # 3- in case of error, fail the process status
283
323
  unless exception_info.nil?
@@ -67,7 +67,10 @@ module Aspera
67
67
  when :delete
68
68
  rest_api.delete(one_res_path)
69
69
  return Main.result_status("deleted")
70
+ else
71
+ raise "unknown action: #{command}"
70
72
  end
73
+ raise "internal error should not reach here"
71
74
  end
72
75
 
73
76
  # implement generic rest operations on given resource path
@@ -77,13 +80,15 @@ module Aspera
77
80
  return entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default,subkey)
78
81
  end
79
82
 
80
- def options;@agents[:options];end
83
+ def options; return @agents[:options];end
81
84
 
82
- def transfer;@agents[:transfer];end
85
+ def transfer; return @agents[:transfer];end
83
86
 
84
- def config;return @agents[:config];end
87
+ def config; return @agents[:config];end
85
88
 
86
- def format;return @agents[:formater];end
89
+ def format; return @agents[:formater];end
90
+
91
+ def persistency; return @agents[:persistency];end
87
92
 
88
93
  end # Plugin
89
94
  end # Cli
@@ -12,7 +12,7 @@ module Aspera
12
12
  command=self.options.get_next_command(ACTIONS)
13
13
  case command
14
14
  when :entitlement
15
- entitlement_id = self.options.get_option(:username,:mandatory),
15
+ entitlement_id = self.options.get_option(:username,:mandatory)
16
16
  customer_id = self.options.get_option(:password,:mandatory)
17
17
  api_metering=AoC.metering_api(entitlement_id,customer_id)
18
18
  return {:type=>:single_object, :data=>api_metering.read('entitlement')[:data]}