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.
data/docs/README.erb.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [comment1]: # (Do not edit this README.md, edit docs/README.erb.md, for details, read docs/README.md)
2
2
  <%
3
3
  # check that required env vars exist, and files
4
- %w{EXENAME GEMSPEC INCL_USAGE INCL_COMMANDS INCL_ASESSION INCL_TRSPEC}.each do |e|
4
+ %w{EXENAME GEMSPEC INCL_USAGE INCL_COMMANDS INCL_ASESSION INCL_DIR_GEM}.each do |e|
5
5
  raise "missing env var #{e}" unless ENV.has_key?(e)
6
6
  raise "missing file #{ENV[e]}" unless File.exist?(ENV[e]) or !e.start_with?('INCL_') #_
7
7
  end
@@ -14,6 +14,29 @@ prsts='['+opprst+'s](#lprt)'
14
14
  prstt=opprst.capitalize # in title
15
15
  gemspec=Gem::Specification::load(ENV["GEMSPEC"]) or raise "error loading #{ENV["GEMSPEC"]}"
16
16
  geminstadd=gemspec.version.to_s.match(/\.[^0-9]/)?' --pre':''
17
+ $LOAD_PATH.unshift(ENV["INCL_DIR_GEM"])
18
+ require 'aspera/fasp/parameters'
19
+ def spec_table
20
+ r='<table><tr><th>Field</th><th>Type</th>'
21
+ Aspera::Fasp::Parameters::SUPPORTED_AGENTS_SHORT.each do |c|
22
+ r << '<th>'<<c.to_s.upcase<<'</th>'
23
+ end
24
+ r << '<th>Description</th></tr>'
25
+ Aspera::Fasp::Parameters.man_table.each do |p|
26
+ p[:description] << (p[:description].empty? ? '' : "\n") << "(" << p[:cli] << ")" unless p[:cli].to_s.empty?
27
+ p.delete(:cli)
28
+ p.keys.each{|c|p[c]='&nbsp;' if p[c].to_s.empty?}
29
+ p[:description].gsub!("\n",'<br/>')
30
+ p[:type].gsub!(',','<br/>')
31
+ r << '<tr><td>'<<p[:name]<<'</td><td>'<<p[:type]<<'</td>'
32
+ Aspera::Fasp::Parameters::SUPPORTED_AGENTS_SHORT.each do |c|
33
+ r << '<td>'<<p[c]<<'</td>'
34
+ end
35
+ r << '<td>'<<p[:description]<<'</td></tr>'
36
+ end
37
+ r << '</table>'
38
+ return r
39
+ end
17
40
  -%>
18
41
  # <%=tool%> : Command Line Interface for IBM Aspera products
19
42
 
@@ -1283,11 +1306,31 @@ A [_transfer-spec_](#transferspec) is a Hash table, so it is described on the co
1283
1306
 
1284
1307
  ## <a name="transferparams"></a>Transfer Parameters
1285
1308
 
1286
- All standard _transfer-spec_ parameters can be overloaded. To display parameters,
1287
- run in debug mode (--log-level=debug). [_transfer-spec_](#transferspec) can
1288
- also be saved/overridden in the config file.
1309
+ All standard _transfer-spec_ parameters can be speficied.
1310
+ [_transfer-spec_](#transferspec) can also be saved/overridden in the config file.
1311
+
1312
+ References:
1313
+
1314
+ * [Aspera Node API Documentation](https://developer.ibm.com/apis/catalog?search=%22aspera%20node%20api%22)&rarr;/opt/transfers
1315
+ * [Aspera Transfer SDK Documentation](https://developer.ibm.com/apis/catalog?search=%22aspera%20transfer%20sdk%22)&rarr;Guides&rarr;API Ref&rarr;Transfer Spec V1
1316
+
1317
+ Parameters can be displayed with commands:
1318
+
1319
+ ```
1320
+ $ <%=cmd%> config ascp spec
1321
+ $ <%=cmd%> config ascp spec --select=@json:'{"f":"Y"}' --fields=-f,n,c
1322
+ ```
1323
+
1324
+ Columns:
1325
+
1326
+ * D=Direct (local `ascp` execution)
1327
+ * N=Node API
1328
+ * C=Connect Client
1329
+ * arg=`ascp` argument or environment variable
1289
1330
 
1290
- <%= File.read(ENV['INCL_TRSPEC']).gsub(/.*<body>(.*)<\/body>.*/m,'\1') %>
1331
+ Fields with EX_ prefix are extensions to transfer agent `direct`. (only in <%=tool%>).
1332
+
1333
+ <%= spec_table %>
1291
1334
 
1292
1335
  ### Destination folder for transfers
1293
1336
 
@@ -1369,7 +1412,7 @@ Source files are located on "Aspera on cloud", when :
1369
1412
 
1370
1413
  ### <a name="multisession"></a>Support of multi-session
1371
1414
 
1372
- Multi session, i.e. starting a transfer of a file set using multiple sessions is supported on "direct" and "node" agents, not yet on connect.
1415
+ Multi session, i.e. starting a transfer of a file set using multiple sessions (one ascp process per session) is supported on "direct" and "node" agents, not yet on connect.
1373
1416
 
1374
1417
  * when agent=node :
1375
1418
 
@@ -1390,6 +1433,7 @@ shall be preferred.
1390
1433
 
1391
1434
  Multi-session spawn is done by <%=tool%>.
1392
1435
 
1436
+ When multi-session is used, one separate UDP port is used per session (refer to `ascp` manual page).
1393
1437
 
1394
1438
  ### Examples
1395
1439
 
@@ -1665,7 +1709,7 @@ To activate default use of JWT authentication for <%=tool%> using the <%=prst%>,
1665
1709
 
1666
1710
  * change auth method to JWT
1667
1711
  * provide location of private key
1668
- * provide username to login as (OAuthg "subject")
1712
+ * provide username to login as (OAuth "subject")
1669
1713
 
1670
1714
  Execute:
1671
1715
 
@@ -2076,19 +2120,40 @@ The activity app can be queried with:
2076
2120
  $ <%=cmd%> aoc admin analytics transfers
2077
2121
  ```
2078
2122
 
2079
- It can also support filters and send notification email with a template:
2123
+ It can also support filters and send notification using option `notif_to`. a template is defined using option `notif_template` :
2124
+
2125
+ `mytemplate.erb`:
2126
+
2127
+ ```
2128
+ From: <%='<'%>%=from_name%> <<%='<'%>%=from_email%>>
2129
+ To: <<%='<'%>%=ev['user_email']%>>
2130
+ Subject: <%='<'%>%=ev['files_completed']%> files received
2131
+
2132
+ Dear <%='<'%>%=ev[:user_email.to_s]%>,
2133
+ We received <%='<'%>%=ev['files_completed']%> files for a total of <%='<'%>%=ev['transferred_bytes']%> bytes, starting with file:
2134
+ <%='<'%>%=ev['content']%>
2135
+
2136
+ Thank you.
2137
+ ```
2138
+ The environment provided contains the following additional variable:
2139
+
2140
+ * ev : all details on the transfer event
2141
+
2142
+ Example:
2080
2143
 
2081
2144
  ```
2082
- $ <%=cmd%> aoc admin analytics transfers --once-only=yes --lock-port=123455 \
2145
+ $ <%=cmd%> aoc admin analytics transfers --once-only=yes --lock-port=12345 \
2083
2146
  --query=@json:'{"status":"completed","direction":"receive"}' \
2084
- --notify=@json:'{"to":"<''%=transfer[:user_email.to_s]%>","subject":"<''%=transfer[:files_completed.to_s]%> files received","body":"Dear <''%=transfer[:user_email.to_s]%>\nWe received <''%=transfer[:files_completed.to_s]%> files for a total of <''%=transfer[:transferred_bytes.to_s]%> bytes, starting with file:\n<''%=transfer[:content.to_s]%>\n\nThank you."}'
2147
+ --notif-to=active --notif-template=@file:mytemplate.erb
2085
2148
  ```
2086
2149
 
2150
+ Options:
2151
+
2087
2152
  * `once_only` keep track of last date it was called, so next call will get only new events
2088
2153
  * `query` filter (on API call)
2089
2154
  * `notify` send an email as specified by template, this could be places in a file with the `@file` modifier.
2090
2155
 
2091
- Note this must not be executed in less than 5 minutes because the analytics interface accepts only a period of time between 5 minutes and 6 months. here the period is [date of previous execution]..[now].
2156
+ Note this must not be executed in less than 5 minutes because the analytics interface accepts only a period of time between 5 minutes and 6 months. The period is [date of previous execution]..[now].
2092
2157
 
2093
2158
  ## Using specific transfer ports
2094
2159
 
@@ -2339,11 +2404,15 @@ $ <%=cmd%> node access_key create --value=@json:'{"id":"eudemo-sedemo","secret":
2339
2404
 
2340
2405
  3 authentication methods are supported:
2341
2406
 
2342
- * boot
2343
- * web
2344
2407
  * jwt
2408
+ * web
2409
+ * boot
2345
2410
 
2346
- For boot method:
2411
+ For JWT, create an API client in Faspex with jwt support, and use: `--auth=jwt`.
2412
+
2413
+ For web method, create an API client in Faspex, and use: --auth=web
2414
+
2415
+ For boot method: (will be removed in future)
2347
2416
 
2348
2417
  * open a browser
2349
2418
  * start developer mode
@@ -2356,25 +2425,57 @@ Use it as password and use `--auth=boot`.
2356
2425
  $ <%=cmd%> conf id f5boot update --url=https://localhost/aspera/faspex --auth=boot --password=ABC.DEF.GHI...
2357
2426
  ```
2358
2427
 
2359
- For web method, create an API client in Faspex, and use: --auth=web
2360
-
2361
- For JWT, create an API client in Faspex with jwt supporot, and use: --auth=jwt
2362
- as of beta£3 this does not allow regular users.
2363
-
2364
2428
  Ready to use Faspex5 with CLI.
2365
2429
 
2366
- Once the graphical registration form exist, ther bootstrap method can be removed.
2367
-
2368
2430
  # Plugin: IBM Aspera Faspex (4.x)
2369
2431
 
2370
2432
  Notes:
2371
2433
 
2372
- * the command "v4" requires the use of APIv4, refer to the Faspex Admin manual on how to activate.
2373
- * for full details on Faspex API, refer to: [Reference on Developer Site](https://www.ibm.com/products/aspera/developer)
2434
+ * The command "v4" requires the use of APIv4, refer to the Faspex Admin manual on how to activate.
2435
+ * For full details on Faspex API, refer to: [Reference on Developer Site](https://developer.ibm.com/apis/catalog/?search=faspex)
2436
+
2437
+ ## Listing Packages
2438
+
2439
+ Command: `faspex package list`
2440
+
2441
+ ### Option `box`
2442
+
2443
+ By default it looks in box `inbox`, but the following boxes are also supported: `archive` and `sent`, selected with option `box`.
2444
+
2445
+ ### Option `recipient`
2446
+
2447
+ A user can receive a package because the recipient is:
2448
+
2449
+ * the user himself (default)
2450
+ * the user is part of a dropbox or a workgroup (select with option `recipient` with value `*<name of WG or DB>`
2451
+
2452
+ ### Option `query`
2453
+
2454
+ As inboxes may be large, it is possible to use the following query parameters:
2455
+
2456
+ * `count` : (native) number items in one API call (default=0, equivalent to 10)
2457
+ * `page` : (native) id of page in call (default=0)
2458
+ * `startIndex` : (native) index of item to start, default=0, oldest index=0
2459
+ * `max` : maximum number of items
2460
+ * `pmax` : maximum number of pages
2461
+
2462
+ (SQL query is `LIMIT <startIndex>, <count>`)
2463
+
2464
+ The API is listed in [Faspex 4 API Reference](https://developer.ibm.com/apis/catalog/?search=faspex) under "Services (API v.3)".
2465
+
2466
+ If no parameter `max` or `pmax` is provided, then all packages will be listed in the inbox, which result in paged API calls (using parameter: `count` and `page`). By default page is `0` (`10`), it can be increased to have less calls.
2467
+
2468
+ ### Example
2469
+
2470
+ ```
2471
+ $ <%=cmd%> faspex package list --box=inbox --recipient='*my_dropbox' --query=@json:'{"max":20,"pmax":2,"count":20}'
2472
+ ```
2473
+
2474
+ List a maximum of 20 items grouped by pages of 20, with maximum 2 pages in received box (inbox) when received in dropbox `*my_dropbox`.
2374
2475
 
2375
2476
  ## Receiving a Package
2376
2477
 
2377
- The command is `package recv`, possible methosd are:
2478
+ The command is `package recv`, possible methods are:
2378
2479
 
2379
2480
  * provide a package id with option `id`
2380
2481
  * provide a public link with option `link`
@@ -2391,7 +2492,7 @@ If the package is in a specific dropbox, add option `recipient` for both the `li
2391
2492
  $ <%=cmd%> faspex package list --recipient='*thedropboxname'
2392
2493
  ```
2393
2494
 
2394
-
2495
+ if `id` is set to `ALL`, then all packages are downloaded, and if option `once_only`is used, then a persistency file is created to keep track of already downloaded packages.
2395
2496
 
2396
2497
  ## Sending a Package
2397
2498
 
@@ -2410,7 +2511,19 @@ Additional optional parameters in `delivery_info`:
2410
2511
  * Package Note: : `"note":"note this and that"`
2411
2512
  * Package Metadata: `"metadata":{"Meta1":"Val1","Meta2":"Val2"}`
2412
2513
 
2413
- ## operation on dropboxes
2514
+ ## Email notification on transfer
2515
+
2516
+ Like for any transfer, a notification can be sent by email using parameters: `notif_to` and `notif_template` .
2517
+
2518
+ Example:
2519
+
2520
+ ```
2521
+ $ <%=cmd%> faspex package send --delivery-info=@json:'{"title":"test pkg 1","recipients":["aspera.user1@gmail.com"]}' ~/Documents/Samples/200KB.1 --notif-to=aspera.user1@gmail.com --notif-template=@ruby:'%Q{From: <%='<'%>%=from_name%> <<%='<'%>%=from_email%>>\nTo: <<%='<'%>%=to%>>\nSubject: Package sent: <%='<'%>%=ts["tags"]["aspera"]["faspex"]["metadata"]["_pkg_name"]%> files received\n\nTo user: <%='<'%>%=ts["tags"]["aspera"]["faspex"]["recipients"].first["email"]%>}'
2522
+ ```
2523
+
2524
+ In this example the notification template is directly provided on command line. Package information placed in the message are directly taken from the tags in transfer spec. The template can be placed in a file using modifier: `@file:`
2525
+
2526
+ ## Operation on dropboxes
2414
2527
 
2415
2528
  Example:
2416
2529
 
@@ -2828,14 +2941,14 @@ Aspera CLI can send email, for that setup SMTP configuration. This is done with
2828
2941
  The `smtp` option is a hash table (extended value) with the following fields:
2829
2942
  <table>
2830
2943
  <tr><th>field</th><th>default</th><th>example</th><th>description</th></tr>
2831
- <tr><td>server</td><td>-</td><td>smtp.gmail.com</td><td>SMTP server address</td></tr>
2832
- <tr><td>tls</td><td>true</td><td>false</td><td>use of TLS</td></tr>
2833
- <tr><td>port</td><td>587 for tls<br/>25 else</td><td>587</td><td>port for service</td></tr>
2834
- <tr><td>domain</td><td>domain of server</td><td>gmail.com</td><td>email domain of user</td></tr>
2835
- <tr><td>username</td><td>-</td><td>john@example.com</td><td>user to authenticate on SMTP server, leave empty for open auth.</td></tr>
2836
- <tr><td>password</td><td>-</td><td>MyP@ssword</td><td>password for above username</td></tr>
2837
- <tr><td>from\_email</td><td>username if defined</td><td>laurent.martin.l@gmail.com</td><td>address used if received replies</td></tr>
2838
- <tr><td>from\_name</td><td>same as email</td><td>John Wayne</td><td>display name of sender</td></tr>
2944
+ <tr><td>`server`</td><td>-</td><td>smtp.gmail.com</td><td>SMTP server address</td></tr>
2945
+ <tr><td>`tls`</td><td>true</td><td>false</td><td>use of TLS</td></tr>
2946
+ <tr><td>`port`</td><td>587 for tls<br/>25 else</td><td>587</td><td>port for service</td></tr>
2947
+ <tr><td>`domain`</td><td>domain of server</td><td>gmail.com</td><td>email domain of user</td></tr>
2948
+ <tr><td>`username`</td><td>-</td><td>john@example.com</td><td>user to authenticate on SMTP server, leave empty for open auth.</td></tr>
2949
+ <tr><td>`password`</td><td>-</td><td>MyP@ssword</td><td>password for above username</td></tr>
2950
+ <tr><td>`from_email`</td><td>username if defined</td><td>laurent.martin.l@gmail.com</td><td>address used if received replies</td></tr>
2951
+ <tr><td>`from_name`</td><td>same as email</td><td>John Wayne</td><td>display name of sender</td></tr>
2839
2952
  </table>
2840
2953
 
2841
2954
  ## Example of configuration:
@@ -2865,13 +2978,52 @@ $ <%=cmd%> config id cli_default set smtp @val:@preset:smtp_google
2865
2978
  $ <%=cmd%> config id default set config cli_default
2866
2979
  ```
2867
2980
 
2981
+ ## Email templates
2982
+
2983
+ Sent emails are built using a template that uses the [ERB](https://www.tutorialspoint.com/ruby/eruby.htm) syntax.
2984
+
2985
+ The template is the full SMTP message, including headers.
2986
+
2987
+ The following variables are defined by default:
2988
+
2989
+ * from_name
2990
+ * from_email
2991
+ * to
2992
+
2993
+ Other variables are defined depending on context.
2994
+
2868
2995
  ## Test
2869
2996
 
2870
2997
  Check settings with `smtp_settings` command. Send test email with `email_test`.
2871
2998
 
2872
2999
  ```
2873
3000
  $ <%=cmd%> config --smtp=@preset:smtp_google smtp
2874
- $ <%=cmd%> config --smtp=@preset:smtp_google email sample.dest@example.com
3001
+ $ <%=cmd%> config --smtp=@preset:smtp_google email --notif-to=sample.dest@example.com
3002
+ ```
3003
+
3004
+ ## Notifications for transfer status
3005
+
3006
+ An e-mail notification can be sent upon transfer success and failure (one email per transfer job, one job being possibly multi session, and possibly after retry).
3007
+
3008
+ To activate, use option `notif_to`.
3009
+
3010
+ A default e-mail template is used, but it can be overriden with option `notif_template`.
3011
+
3012
+ The environment provided contains the following additional variables:
3013
+
3014
+ * subject
3015
+ * body
3016
+ * global_transfer_status
3017
+ * ts
3018
+
3019
+ Example of template:
3020
+
3021
+ ```
3022
+ From: <%='<'%>%=from_name%> <<%='<'%>%=from_email%>>
3023
+ To: <<%='<'%>%=to%>>
3024
+ Subject: <%='<'%>%=subject%>
3025
+
3026
+ Transfer is: <%='<'%>%=global_transfer_status%>
2875
3027
  ```
2876
3028
 
2877
3029
  # Tool: `asession`
@@ -2982,7 +3134,7 @@ Interesting ascp features are found in its arguments: (see ascp manual):
2982
3134
  Note that:
2983
3135
 
2984
3136
  * <%=tool%> takes transfer parameters exclusively as a transfer_spec, with `--ts` parameter.
2985
- * not all native ascp arguments are available as standard transfer_spec parameters
3137
+ * most, but not all native ascp arguments are available as standard transfer_spec parameters
2986
3138
  * native ascp arguments can be provided with the [_transfer-spec_](#transferspec) parameter: EX_ascp_args (array), only for the "local" transfer agent (not connect or node)
2987
3139
 
2988
3140
  ### server side and configuration
@@ -3081,6 +3233,18 @@ So, it evolved into <%=tool%>:
3081
3233
 
3082
3234
  * <%= gemspec.version.to_s %>
3083
3235
 
3236
+ * new: `faspex package list` retrieves the whole list, not just first page
3237
+ * new: support web based auth to aoc and faspex 5 using HTTPS, new dependency on gem `webrick`
3238
+ * new: the error "Remote host is not who we expected" displays a special remediation message
3239
+ * new: `conf ascp spec` displays supported transfer spec
3240
+ * new: options `notif_to` and `notif_template` to send email notifications on transfer (and other events)
3241
+ * fix: space character in `faspe:` url are precent encoded if needed
3242
+ * fix: `preview scan`: if file_id is unknown, ignore and continue scan
3243
+ * change: for commands that potentially execute several transfers (`package recv --id=ALL`), if one transfer fails then <%=tool%> exits with code 1 (instead of zero=success)
3244
+ * change: (break) option `notify` or `aoc` replaced with `notif_to` and `notif_template`
3245
+
3246
+ * 4.2.1
3247
+
3084
3248
  * new: command `faspex package recv` supports link of type: `faspe:`
3085
3249
  * new: command `faspex package recv` supports option `recipient` to specify dropbox with leading `*`
3086
3250
 
@@ -3503,13 +3667,17 @@ You may either install the suggested Gems, or remove your ed25519 key from your
3503
3667
 
3504
3668
  ## Error "Remote host is not who we expected"
3505
3669
 
3506
- `ascp` version 4.x changed the algorithm used to check the SSH server certificate. To ignore the certificate (SSH fingerprint) add option on client side:
3670
+ Cause: `ascp` >= 4.x checks fingerprint of highest server host key, including ECDSA. `ascp` < 4.0 (3.9.6 and earlier) support only to RSA level (and ignore ECDSA presented by server). `aspera.conf` supports a single fingerprint.
3671
+
3672
+ Workaround on client side: To ignore the certificate (SSH fingerprint) add option on client side (this option can also be added permanently to the config file):
3507
3673
 
3508
3674
  ```
3509
3675
  --ts=@json:'{"sshfp":null}'
3510
3676
  ```
3511
3677
 
3512
- Refer to ES-1944 in release notes of 4.1 and to [HSTS admin manual section "Configuring Transfer Server Authentication With a Host-Key Fingerprint"](https://www.ibm.com/docs/en/ahts/4.2?topic=upgrades-configuring-ssh-server): if you have access to server side, basically disable other SSH host keys than RSA.
3678
+ Workaround on server side: Either remove the fingerprint from `aspera.conf`, or keep only RSA host keys in `sshd_config`.
3679
+
3680
+ References: ES-1944 in release notes of 4.1 and to [HSTS admin manual section "Configuring Transfer Server Authentication With a Host-Key Fingerprint"](https://www.ibm.com/docs/en/ahts/4.2?topic=upgrades-configuring-ssh-server).
3513
3681
 
3514
3682
  ## Miscelaneous
3515
3683
 
data/docs/test_env.conf CHANGED
@@ -5,7 +5,7 @@ default:
5
5
  config: cli_default
6
6
  aoc: tst_aoc1
7
7
  faspex: tst_faspex
8
- faspex5: tst_faspex5_web
8
+ faspex5: tst_faspex5
9
9
  shares: tst_shares_1
10
10
  shares2: tst_shares2
11
11
  node: tst_node
@@ -65,6 +65,7 @@ tst_faspex5:
65
65
  client_secret: your value here
66
66
  private_key: your value here
67
67
  username: your value here
68
+ password: your value here
68
69
  tst_shares:
69
70
  url: your value here
70
71
  username: your value here
data/examples/faspex4.rb CHANGED
@@ -1,29 +1,37 @@
1
1
  #!/usr/bin/env ruby
2
+ # find Faspex API here: https://developer.ibm.com/apis/catalog/?search=faspex
3
+ # this example makes use of class Aspera::Rest for REST calls, alternatively class RestClient of gem rest-client could be used
4
+ # this example makes use of class Aspera::Fasp::Local for transfers, alternatively the official "Transfer SDK" could be used
5
+ # Aspera SDK can be downloaded with: `ascli conf ascp install` , it installs in $HOME/.aspera/ascli/sdk
2
6
  require 'aspera/rest'
3
7
  require 'aspera/log'
4
8
  require 'aspera/fasp/local'
5
- require 'aspera/cli/listener/line_dump'
6
- require 'aspera/cli/extended_value'
7
9
 
8
- # set high log level for the example
10
+ tmpdir=ENV['tmp']||Dir.tmpdir || '.'
11
+
12
+ # Set high log level for the example, decrease to :warn usually
9
13
  Aspera::Log.instance.level=:debug
10
14
 
11
- # set folder where SDK is installed
15
+ # Set folder where SDK is installed (mandatory)
12
16
  # (if ascp is not there, the lib will try to find in usual locations)
13
- Aspera::Fasp::Installation.instance.folder='.'
17
+ # (if data files are not there, they will be created)
18
+ Aspera::Fasp::Installation.instance.folder = tmpdir
14
19
 
15
20
  if ! ARGV.length.eql?(3)
16
- Aspera::Log.log.error("wrong number of args: #{ARGV.length}")
21
+ Aspera::Log.log.error("Wrong number of args: #{ARGV.length}")
17
22
  Aspera::Log.log.error("Usage: #{$0} <faspex URL> <faspex username> <faspex password>")
18
23
  Aspera::Log.log.error("Example: #{$0} https://faspex.com/aspera/faspex john p@sSw0rd")
19
24
  Process.exit(1)
20
25
  end
21
26
 
22
- faspex_url=ARGV[0]
27
+ faspex_url=ARGV[0] # typically: https://faspex.example.com/aspera/faspex
23
28
  faspex_user=ARGV[1]
24
29
  faspex_pass=ARGV[2]
25
30
 
26
- # 1: demo API v3
31
+ # comment out this if certificate is valid, keep line to ignore certificate
32
+ Aspera::Rest.insecure=true
33
+
34
+ # 1: Faspex 4 API v3
27
35
  #---------------
28
36
 
29
37
  # create REST API object
@@ -35,34 +43,36 @@ api_v3=Aspera::Rest.new({
35
43
  :password => faspex_pass
36
44
  }})
37
45
 
46
+ # very simple api call
38
47
  api_v3.read('me')
39
48
 
40
49
  # 2: send a package
41
50
  #---------------
42
51
 
43
52
  # create a sample file to send
44
- file_to_send='./myfile.bin'
53
+ file_to_send=File.join(tmpdir,'myfile.bin')
45
54
  File.open(file_to_send, "w") {|f| f.write("sample data") }
46
55
  # package creation parameters
47
56
  package_create_params={'delivery'=>{'title'=>'test package','recipients'=>['aspera.user1@gmail.com'],'sources'=>[{'paths'=>[file_to_send]}]}}
48
57
  pkg_created=api_v3.create('send',package_create_params)[:data]
49
- # get transfer specification
58
+ # get transfer specification (normally: only one)
50
59
  transfer_spec=pkg_created['xfer_sessions'].first
51
60
  # set paths of files to send
52
- transfer_spec['paths']=['source'=>file_to_send]
61
+ transfer_spec['paths']=[{'source'=>file_to_send}]
53
62
  # get the local agent (i.e. ascp)
54
- client=Aspera::Fasp::Local.new
55
- # disable ascp output on stdout to not mix with JSON events
56
- client.quiet=true
63
+ transfer_client=Aspera::Fasp::Local.new
64
+ # disable ascp output on stdout (optional)
65
+ transfer_client.quiet=true
57
66
  # start transfer (asynchronous)
58
- job_id=client.start_transfer(transfer_spec)
59
- result=client.wait_for_transfers_completion
67
+ job_id=transfer_client.start_transfer(transfer_spec)
68
+ # wait for all transfer completion (for the example)
69
+ result=transfer_client.wait_for_transfers_completion
60
70
  # notify of any transfer error
61
71
  result.select{|i|!i.eql?(:success)}.each do |e|
62
72
  Aspera::Log.log.error("A transfer error occured: #{e.message}")
63
73
  end
64
74
 
65
- # 1: demo API v4
75
+ # 3: Faspex 4 API v4
66
76
  #---------------
67
77
  api_v4=Aspera::Rest.new({
68
78
  :base_url => faspex_url+'/api',
@@ -75,4 +85,5 @@ api_v4=Aspera::Rest.new({
75
85
  :scope => 'admin'
76
86
  }})
77
87
 
88
+ # Use it. Note that Faspex 4 API v4 is totally different from Faspex 4 v3 APIs, see ref on line 2
78
89
  Aspera::Log.dump('users',api_v4.read('users')[:data])
@@ -21,15 +21,17 @@ module Aspera
21
21
  private
22
22
  # name of application, also foldername where config is stored
23
23
  PROGRAM_NAME = 'ascli'
24
+ # name of the containing gem, same as in <gem name>.gemspec
24
25
  GEM_NAME = 'aspera-cli'
25
- VERBOSE_LEVELS=[:normal,:minimal,:quiet]
26
-
27
- 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'
28
30
 
31
+ private_constant :PROGRAM_NAME,:GEM_NAME,:HELP_URL,:GEM_URL
29
32
  # =============================================================
30
33
  # Parameter handlers
31
34
  #
32
-
33
35
  def option_insecure; Rest.insecure ; end
34
36
 
35
37
  def option_insecure=(value); Rest.insecure = value; end
@@ -40,20 +42,19 @@ module Aspera
40
42
 
41
43
  # minimum initialization
42
44
  def initialize(argv)
43
- # first thing : manage debug level (allows debugging or option parser)
45
+ # first thing : manage debug level (allows debugging of option parser)
44
46
  early_debug_setup(argv)
47
+ # compare $0 with expected name
45
48
  current_prog_name=File.basename($PROGRAM_NAME)
46
49
  unless current_prog_name.eql?(PROGRAM_NAME)
47
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")
48
51
  end
49
- # overriding parameters on transfer spec
50
52
  @option_help=false
51
53
  @bash_completion=false
52
54
  @option_show_config=false
55
+ # environment provided to plugin for various capabilities
53
56
  @plugin_env={}
54
- @help_url='http://www.rubydoc.info/gems/'+GEM_NAME
55
- @gem_url='https://rubygems.org/gems/'+GEM_NAME
56
- # give command line arguments to option manager (no parsing)
57
+ # find out application main folder
57
58
  app_main_folder=ENV[conf_dir_env_var]
58
59
  # if env var undefined or empty
59
60
  if app_main_folder.nil? or app_main_folder.empty?
@@ -61,20 +62,21 @@ module Aspera
61
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)
62
63
  app_main_folder=File.join(user_home_folder,Plugins::Config::ASPERA_HOME_FOLDER_NAME,PROGRAM_NAME)
63
64
  end
65
+ # give command line arguments to option manager (no parsing)
64
66
  @plugin_env[:options]=@opt_mgr=Manager.new(PROGRAM_NAME,argv,app_banner())
65
67
  @plugin_env[:formater]=Formater.new(@plugin_env[:options])
66
68
  Rest.user_agent=PROGRAM_NAME
67
- # must override help methods before parser called (in other constructors)
69
+ # declare and parse global options
68
70
  init_global_options()
69
71
  # secret manager
70
72
  @plugin_env[:secret]=Aspera::Secrets.new
71
73
  # the Config plugin adds the @preset parser
72
- @plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,@help_url,Aspera::Cli::VERSION,app_main_folder)
74
+ @plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,HELP_URL,Aspera::Cli::VERSION,app_main_folder)
73
75
  # the TransferAgent plugin may use the @preset parser
74
76
  @plugin_env[:transfer]=TransferAgent.new(@plugin_env)
75
- Log.log.debug('created plugin env'.red)
76
77
  # set application folder for modules
77
78
  @plugin_env[:persistency]=PersistencyFolder.new(File.join(@plugin_env[:config].main_folder,'persist_store'))
79
+ Log.log.debug('plugin env created'.red)
78
80
  Oauth.persist_mgr=@plugin_env[:persistency]
79
81
  Fasp::Parameters.file_list_folder=File.join(@plugin_env[:config].main_folder,'filelists')
80
82
  Aspera::RestErrorAnalyzer.instance.log_file=File.join(@plugin_env[:config].main_folder,'rest_exceptions.log')
@@ -88,9 +90,9 @@ module Aspera
88
90
  banner << "\t#{PROGRAM_NAME} COMMANDS [OPTIONS] [ARGS]\n"
89
91
  banner << "\nDESCRIPTION\n"
90
92
  banner << "\tUse Aspera application to perform operations on command line.\n"
91
- banner << "\tDocumentation and examples: #{@gem_url}\n"
93
+ banner << "\tDocumentation and examples: #{GEM_URL}\n"
92
94
  banner << "\texecute: #{PROGRAM_NAME} conf doc\n"
93
- banner << "\tor visit: #{@help_url}\n"
95
+ banner << "\tor visit: #{HELP_URL}\n"
94
96
  banner << "\nENVIRONMENT VARIABLES\n"
95
97
  banner << "\t#{conf_dir_env_var} config folder, default: $HOME/#{Plugins::Config::ASPERA_HOME_FOLDER_NAME}/#{PROGRAM_NAME}\n"
96
98
  banner << "\t#any option can be set as an environment variable, refer to the manual\n"
@@ -188,6 +190,8 @@ module Aspera
188
190
 
189
191
  protected
190
192
 
193
+ # env var name to override the app's main folder
194
+ # default main folder is $HOME/<vendor main app folder>/<program name>
191
195
  def conf_dir_env_var
192
196
  return "#{PROGRAM_NAME}_home".upcase
193
197
  end
@@ -219,10 +223,28 @@ module Aspera
219
223
  return Main.result_nothing
220
224
  end
221
225
 
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
240
+
222
241
  # this is the main function called by initial script just after constructor
223
242
  def process_command_line
224
243
  Log.log.debug('process_command_line')
244
+ # catch exception information , if any
225
245
  exception_info=nil
246
+ # false if command shall not be executed ("once_only")
247
+ execute_command=true
226
248
  begin
227
249
  # find plugins, shall be after parse! ?
228
250
  @plugin_env[:config].add_plugins_from_lookup_folders
@@ -264,11 +286,12 @@ module Aspera
264
286
  Log.log.debug("Opening lock port #{lock_port.to_i}")
265
287
  @tcp_server=TCPServer.new('127.0.0.1',lock_port.to_i)
266
288
  rescue => e
267
- raise CliError,"Another instance is already running (#{e.message})."
289
+ execute_command=false
290
+ Log.log.warn("Another instance is already running (#{e.message}).")
268
291
  end
269
292
  end
270
- # execute and display
271
- @plugin_env[:formater].display_results(command_plugin.execute_action)
293
+ # execute and display (if not exclusive execution)
294
+ @plugin_env[:formater].display_results(command_plugin.execute_action) if execute_command
272
295
  # finish
273
296
  @plugin_env[:transfer].shutdown
274
297
  rescue CliBadArgument => e; exception_info=[e,'Argument',:usage]
@@ -286,10 +309,15 @@ module Aspera
286
309
  unless exception_info.nil?
287
310
  @plugin_env[:formater].display_message(:error,"ERROR:".bg_red.gray.blink+" "+exception_info[1]+": "+exception_info[0].message)
288
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
289
315
  end
290
316
  # 2- processing of command not processed (due to exception or bad command line)
291
- @opt_mgr.final_errors.each do |msg|
292
- @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
293
321
  end
294
322
  # 3- in case of error, fail the process status
295
323
  unless exception_info.nil?