Class: Aspera::Cli::Plugins::Node

Inherits:
BasicAuth show all
Includes:
SyncActions
Defined in:
lib/aspera/cli/plugins/node.rb

Constant Summary collapse

COMMANDS_GEN3_SPEC =

DSL metadata for Gen3 root commands (description:, arguments:, transfer_paths:, aliases:). :access_keys is skipped (intermediate node declared separately), :sync is declared separately. action: entries are in GEN3_NODE_ACTIONS, or implicit.

{
  search:      {description: 'Search for files',             arguments: [{name: :path}]},
  space:       {description: 'Show space information',       arguments: [{name: :paths, multiple: true}]},
  mkdir:       {description: 'Create a folder (Gen3)',       arguments: [{name: :paths, multiple: true}]},
  mklink:      {description: 'Create a symbolic link (Gen3)', arguments: [{name: :target}, {name: :link_path}]},
  mkfile:      {description: 'Create a file (Gen3)', arguments: [{name: :path}, {name: :contents, mandatory: false, default: nil}]},
  rename:      {description: 'Rename a file or folder (Gen3)', arguments: [{name: :folder}, {name: :source}, {name: :destination}]},
  delete:      {description: 'Delete files or folders (Gen3)', arguments: [{name: :paths, multiple: true}]},
  ls:          {description: 'List files (Gen3)',            arguments: [{name: :path}], aliases: [:browse]},
  upload:      {description: 'Upload files (Gen3)',          transfer_paths: :send},
  download:    {description: 'Download files (Gen3)',        transfer_paths: :receive},
  cat:         {description: 'Show file contents (Gen3)',    arguments: [{name: :path}]},
  transport:   {description: 'Show transport parameters'},
  spec:        {description: 'Show transfer spec base'},
  api_details: {description: 'Show API details'},
  health:      {description: 'Check node health'},
  events:      {description: 'List events'},
  info:        {description: 'Show node info'},
  slash:       {description: 'Show root info'},
  license:     {description: 'Show license'},
  access_keys: {description: 'Manage access keys'}
}.freeze
NODE4_READ_ACTIONS =

Gen4 read commands also exposed on AoC packages (aoc packages ls <id>)

%i[bearer_token_node node_info ls find].freeze
COMMANDS_GEN4_SPEC =
{
  mkdir:             {description: 'Create folder',                  arguments: SINGLE_PATH_ARG},
  mklink:            {description: 'Create symbolic link',           arguments: SINGLE_PATH_ARG},
  mkfile:            {description: 'Create file',                    arguments: [{name: :path}, {name: :contents, mandatory: false, default: nil}]},
  rename:            {description: 'Rename entry',                   arguments: [{name: :source_path}, {name: :new_name}]},
  delete:            {description: 'Delete entry',                   arguments: [{name: :path, bulk: true}]},
  upload:            {description: 'Upload files',                   transfer_paths: :send},
  download:          {description: 'Download files',                 transfer_paths: :receive},
  modify:            {description: 'Modify file',                    arguments: [{name: :path}, {name: :file, type: Hash, schema: 'node:components.schemas.files-id-put-request'}]},
  cat:               {description: 'Show file contents',             arguments: SINGLE_PATH_ARG},
  show:              {description: 'Show file info',                 arguments: SINGLE_PATH_ARG},
  thumbnail:         {description: 'Show file thumbnail',            arguments: SINGLE_PATH_ARG},
  bearer_token_node: {description: 'Show bearer token for file node', arguments: SINGLE_PATH_ARG},
  node_info:         {description: 'Show node info for file',        arguments: SINGLE_PATH_ARG},
  ls:                {description: 'List files',                     arguments: SINGLE_PATH_ARG, aliases: [:browse]},
  find:              {description: 'Find files',                     arguments: SINGLE_PATH_ARG + FILTER_ARGS}
}.freeze
COMMANDS_COS =

Root commands exposed by cos node and shares files (mount: only:)

%i[upload download info access_keys api_details transfer].freeze
COMMANDS_SHARES =
%i[api_details space mkdir mklink mkfile rename delete ls upload download cat sync transport spec].freeze
GEN4_LS_FIELDS =

browse display fields for gen4

%w[name type recursive_size size modified_time access_level].freeze

Constants included from SyncActions

SyncActions::ADMIN_COMMANDS, SyncActions::PATH_AND_INFO_ARGS, SyncActions::STATE_STR

Constants inherited from Base

Base::FILTER_ARGS

Instance Attribute Summary

Attributes inherited from Base

#context, #help_path

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SyncActions

#action_sync_admin_file_info, #async_info_from_args, #db_from_args, included, register_sync_admin_commands, #run_sync_transfer

Methods inherited from BasicAuth

#basic_auth_api, #basic_auth_params

Methods inherited from Base

#action_for, #add_manual_header, application_name, #bulk_result, command, command_registry, commands_under, #config, crud_commands, declare_options, define_action_method, #dispatch_child, #dispatch_from_registry, #dispatch_leaf, #dispatch_mount, #entity_create, #entity_delete, #entity_list, #entity_modify, entity_noun, #entity_res_path, #entity_show, #execute_action, #execute_leaf, file_matcher, #formatter, #generate_help, #http_config, #invoke_action, operation_description, option_sources, option_specs, #options, #persistency, #presets, #progress_bar, #query_read_delete, register_option_spec, #resolve_argument, #resolve_arguments, #transfer, use_options, used_option_sources, #validate_registry

Methods included from OptionDeclarator

#declare_options, #option, #option_specs, #register_option_spec

Constructor Details

#initialize(context:, api: nil) ⇒ Node

Returns a new instance of Node.

Parameters:

  • (defaults to: nil)

    an existing API object for the Node API



170
171
172
173
174
# File 'lib/aspera/cli/plugins/node.rb', line 170

def initialize(context:, api: nil)
  super(context: context)
  # Api::Node or Rest::Client (Shares), when provided by the host plugin
  @api_node = api
end

Class Method Details

.detect(address_or_url) ⇒ Hash, NilClass

Returns:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/aspera/cli/plugins/node.rb', line 37

def detect(address_or_url)
  urls = if address_or_url.match?(%r{^[a-z]{1,6}://})
    [address_or_url]
  else
    [
      "https://#{address_or_url}",
      "https://#{address_or_url}:9092",
      "http://#{address_or_url}:9091"
    ]
  end
  error = nil
  urls.each do |base_url|
    next unless base_url.match?(%r{^https?://})
    api = Rest::Client.new(base_url: base_url)
    test_endpoint = 'ping'
    http = api.read(test_endpoint, ret: :resp)
    next unless http.body.eql?('')
    # also remove "/"
    url_end = -2 - test_endpoint.length
    return {
      url:     http.uri.to_s[0..url_end],
      version: 'requires authentication'
    }
  rescue StandardError => e
    error = e
    Log.log.debug { "detect error: #{e}" }
  end
  raise error if error
  return
end

.gen3_entry_folder?(entry) ⇒ Boolean

Using /files/browse: is it a folder (node and shares)

Returns:



69
70
71
# File 'lib/aspera/cli/plugins/node.rb', line 69

def gen3_entry_folder?(entry)
  FOLDER_TYPES.include?(entry['type'])
end

Instance Method Details

#action_access_keys_do_bearer_token_node(path:, do_root_file_id:) ⇒ Object



871
872
873
874
875
876
877
# File 'lib/aspera/cli/plugins/node.rb', line 871

def action_access_keys_do_bearer_token_node(path:, do_root_file_id:, **)
  apifid, result = gen4_apifid_info(do_root_file_id, path)
  Log.dump(:result, result)
  Aspera.assert(apifid.node_api.auth_params[:type].eql?(:oauth2), type: BadArgument) { "Cannot get bearer token if authenticating with secret (#{apifid.node_api.auth_params[:type]})" }
  Aspera.assert(OAuth::Factory.bearer_auth?(result[:password]), 'Not using bearer token auth')
  Result::Text.new(result[:password])
end

#action_access_keys_do_cat(path:, do_root_file_id:) ⇒ Object

access_keys > do > cat



772
773
774
775
# File 'lib/aspera/cli/plugins/node.rb', line 772

def action_access_keys_do_cat(path:, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  Result::Text.new(apifid.node_api.read("files/#{apifid.file_id}/content", ret: :resp).body)
end

#action_access_keys_do_delete(path:, do_root_file_id:) ⇒ Object

access_keys > do > delete



804
805
806
807
808
809
810
811
812
813
814
815
# File 'lib/aspera/cli/plugins/node.rb', line 804

def action_access_keys_do_delete(path:, do_root_file_id:, **)
  bulk_result(path, command: :delete, id_result: 'path') do |l_path|
    apifid = if (m = Parser.percent_selector(l_path))
      Aspera.assert_values(m[:field], ['id'], type: BadIdentifier)
      Api::NodeFileId.new(api_node, m[:value])
    else
      api_node.resolve_api_fid(do_root_file_id, l_path)
    end
    apifid.node_api.delete("files/#{apifid.file_id}")
    {'path' => l_path}
  end
end

#action_access_keys_do_download(do_root_file_id:) ⇒ Object

access_keys > do > download



839
840
841
842
# File 'lib/aspera/cli/plugins/node.rb', line 839

def action_access_keys_do_download(do_root_file_id:, **)
  apifid, source_paths = api_node.resolve_api_fid_paths(do_root_file_id, transfer.ts_source_paths)
  Runner.result_transfer(transfer.start(apifid.node_api.transfer_spec_gen4(apifid.file_id, Transfer::Spec::DIRECTION_RECEIVE, {'paths'=>source_paths})))
end

#action_access_keys_do_find(path:, filter: nil, do_root_file_id:) ⇒ Object

access_keys > do > find



766
767
768
769
# File 'lib/aspera/cli/plugins/node.rb', line 766

def action_access_keys_do_find(path:, filter: nil, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  Result::ObjectList.new(api_node.find_files(apifid.file_id, Base.file_matcher(filter)), fields: ['path'])
end

#action_access_keys_do_ls(path:, do_root_file_id:) ⇒ Object

access_keys > do > ls



758
759
760
761
762
763
# File 'lib/aspera/cli/plugins/node.rb', line 758

def action_access_keys_do_ls(path:, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  file_info = apifid.node_api.read("files/#{apifid.file_id}", headers: Api::Node.add_cache_control)
  return Result::ObjectList.new([file_info], fields: GEN4_LS_FIELDS) unless file_info['type'].eql?('folder')
  Result::ObjectList.new(apifid.node_api.list_files(apifid.file_id, query: query_read_delete), fields: GEN4_LS_FIELDS)
end

#action_access_keys_do_mkdir(path:, do_root_file_id:) ⇒ Object



908
909
910
911
912
# File 'lib/aspera/cli/plugins/node.rb', line 908

def action_access_keys_do_mkdir(path:, do_root_file_id:, **)
  apifid, payload = gen4_mk_resolve(do_root_file_id, path)
  payload[:type] = :folder
  Result::SingleObject.new(apifid.node_api.create("files/#{apifid.file_id}/files", payload))
end

#action_access_keys_do_mkfile(path:, contents:, do_root_file_id:) ⇒ Object



922
923
924
925
926
927
# File 'lib/aspera/cli/plugins/node.rb', line 922

def action_access_keys_do_mkfile(path:, contents:, do_root_file_id:, **)
  apifid, payload = gen4_mk_resolve(do_root_file_id, path)
  payload[:type] = :file
  payload[:contents] = contents.nil? ? '' : Base64.strict_encode64(contents)
  Result::SingleObject.new(apifid.node_api.create("files/#{apifid.file_id}/files", payload))
end


914
915
916
917
918
919
920
# File 'lib/aspera/cli/plugins/node.rb', line 914

def action_access_keys_do_mklink(path:, do_root_file_id:, **)
  apifid, payload = gen4_mk_resolve(do_root_file_id, path)
  payload[:type] = :link
  Aspera.assert(payload[:target_id], 'Missing target_id')
  Aspera.assert(payload[:target_node_id], 'Missing target_node_id')
  Result::SingleObject.new(apifid.node_api.create("files/#{apifid.file_id}/files", payload))
end

#action_access_keys_do_modify(path:, file:, do_root_file_id:) ⇒ Object

access_keys > do > modify



784
785
786
787
788
# File 'lib/aspera/cli/plugins/node.rb', line 784

def action_access_keys_do_modify(path:, file:, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  apifid.node_api.update("files/#{apifid.file_id}", file)
  Result::Status.new('Done')
end

#action_access_keys_do_node_info(path:, do_root_file_id:) ⇒ Object



866
867
868
869
# File 'lib/aspera/cli/plugins/node.rb', line 866

def action_access_keys_do_node_info(path:, do_root_file_id:, **)
  _apifid, result = gen4_apifid_info(do_root_file_id, path)
  Result::SingleObject.new(result)
end

#action_access_keys_do_permission_create(permission:, apifid:) ⇒ Object



947
948
949
950
951
952
953
954
955
956
957
# File 'lib/aspera/cli/plugins/node.rb', line 947

def action_access_keys_do_permission_create(permission:, apifid:, **)
  create_param = permission
  Aspera.assert(!create_param.key?('file_id'), type: Cli::BadArgument) { 'no file_id' }
  create_param['file_id'] = apifid.file_id
  create_param['access_levels'] = Api::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
  the_app = apifid.node_api.app_info
  the_app&.api&.permissions_set_create_params(perm_data: create_param, app_info: the_app)
  created_data = apifid.node_api.create('permissions', create_param)
  the_app&.api&.permissions_send_event(event_data: created_data, app_info: the_app)
  Result::SingleObject.new(created_data)
end

#action_access_keys_do_permission_delete(permission_id:, apifid:) ⇒ Object



938
939
940
941
942
943
944
945
# File 'lib/aspera/cli/plugins/node.rb', line 938

def action_access_keys_do_permission_delete(permission_id:, apifid:, **)
  bulk_result(permission_id, command: :delete) do |one_id|
    apifid.node_api.delete("permissions/#{one_id}")
    the_app = apifid.node_api.app_info
    the_app&.api&.permissions_send_event(event_data: {}, app_info: the_app, types: ['permission.deleted'])
    {'id' => one_id}
  end
end

#action_access_keys_do_permission_list(apifid:) ⇒ Object

access_keys > do > permission > list/show/create/modify/delete



930
931
932
933
934
935
936
# File 'lib/aspera/cli/plugins/node.rb', line 930

def action_access_keys_do_permission_list(apifid:, **)
  list_query = query_read_delete(default: Rest.php_style({'include' => %w[access_level permission_count]}))
  # Specify file to get permissions for unless not specified (then, get all permissions)
  list_query['file_id'] = apifid.file_id unless apifid.file_id.to_s.empty?
  list_query['inherited'] = false if list_query.key?('file_id') && !list_query.key?('inherited')
  Result::ObjectList.new(apifid.node_api.read_with_pages('permissions', list_query))
end

#action_access_keys_do_rename(source_path:, new_name:, do_root_file_id:) ⇒ Object

access_keys > do > rename



797
798
799
800
801
# File 'lib/aspera/cli/plugins/node.rb', line 797

def action_access_keys_do_rename(source_path:, new_name:, do_root_file_id:, **)
  apifid = api_node.resolve_api_fid(do_root_file_id, source_path)
  apifid.node_api.update("files/#{apifid.file_id}", {name: new_name})
  Result::Status.new("renamed to #{new_name}")
end

#action_access_keys_do_show(path:, do_root_file_id:) ⇒ Object

access_keys > do > show



778
779
780
781
# File 'lib/aspera/cli/plugins/node.rb', line 778

def action_access_keys_do_show(path:, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  Result::SingleObject.new(apifid.node_api.read("files/#{apifid.file_id}"))
end

#action_access_keys_do_thumbnail(path:, do_root_file_id:) ⇒ Object

access_keys > do > thumbnail



791
792
793
794
# File 'lib/aspera/cli/plugins/node.rb', line 791

def action_access_keys_do_thumbnail(path:, do_root_file_id:, **)
  apifid = apifid_from_path(do_root_file_id, path)
  Result::Image.new(apifid.node_api.read("files/#{apifid.file_id}/preview", headers: {'Accept' => 'image/png'}, ret: :resp).body)
end

#action_access_keys_do_upload(do_root_file_id:) ⇒ Object

access_keys > do > upload



833
834
835
836
# File 'lib/aspera/cli/plugins/node.rb', line 833

def action_access_keys_do_upload(do_root_file_id:, **)
  apifid = api_node.resolve_api_fid(do_root_file_id, transfer.destination_folder(Transfer::Spec::DIRECTION_SEND), true)
  Runner.result_transfer(transfer.start(apifid.node_api.transfer_spec_gen4(apifid.file_id, Transfer::Spec::DIRECTION_SEND)))
end

#action_access_keys_set_bearer_key(access_key_id:, bearer_key_pem:) ⇒ Object

access_keys > set_bearer_key



960
961
962
963
964
965
966
# File 'lib/aspera/cli/plugins/node.rb', line 960

def action_access_keys_set_bearer_key(access_key_id:, bearer_key_pem:, **)
  access_key_id = api_node.read('access_keys/self')['id'] if access_key_id.eql?('self')
  key = OpenSSL::PKey.read(bearer_key_pem)
  key = key.public_key if key.private?
  api_node.update("access_keys/#{access_key_id}", {token_verification_key: key.to_pem})
  Result::Status.new('public key updated')
end

#action_asperabrowser ⇒ Object



1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
# File 'lib/aspera/cli/plugins/node.rb', line 1122

def action_asperabrowser(**)
  browse_params = {
    'nodeUser' => options.get_option(:username, mandatory: true),
    'nodePW'   => options.get_option(:password, mandatory: true),
    'nodeURL'  => options.get_option(:url, mandatory: true)
  }
  # encode parameters so that it looks good in url
  encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
  Environment.instance.open_uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
  return Result::Status.new('done')
end

#action_async_bandwidth(async_id:) ⇒ Object



982
983
984
985
986
987
988
989
# File 'lib/aspera/cli/plugins/node.rb', line 982

def action_async_bandwidth(async_id:, **)
  Integer(async_id)
  post_data = {'syncs' => [async_id], 'seconds' => 100}
  resp = api_node.create('async/bandwidth', post_data)
  data = resp['bandwidth_data']
  return Result::Empty.new if data.empty?
  Result::ObjectList.new(data.first[async_id]['data'])
end

#action_async_counters(async_id:) ⇒ Object



1016
1017
1018
1019
1020
1021
# File 'lib/aspera/cli/plugins/node.rb', line 1016

def action_async_counters(async_id:, **)
  Integer(async_id)
  resp = api_node.create('async/counters', {'syncs' => [async_id]})['sync_counters'].first[async_id].last
  return Result::Empty.new if resp.nil?
  Result::SingleObject.new(resp)
end

#action_async_files(async_id:) ⇒ Object



991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
# File 'lib/aspera/cli/plugins/node.rb', line 991

def action_async_files(async_id:, **)
  Integer(async_id)
  post_data = {'syncs' => [async_id]}
  # not a URL query: merged into the POST body of async/files
  filter = options.get_option(:query)
  post_data.merge!(filter) unless filter.nil?
  resp = api_node.create('async/files', post_data)
  data = resp['sync_files']
  data = data.first[async_id] unless data.empty?
  iteration_data = []
  skip_ids_persistency = nil
  if options.get_option(:once_only, mandatory: true)
    skip_ids_persistency = PersistencyActionOnce.new(
      manager: persistency,
      data:    iteration_data,
      id:      IdGenerator.from_list('sync_files', options.get_option(:url, mandatory: true), options.get_option(:username, mandatory: true), async_id)
    )
    data.select! { |l| l['fnid'].to_i > iteration_data.first } unless iteration_data.first.nil?
    iteration_data[0] = data.last['fnid'].to_i unless data.empty?
  end
  return Result::Empty.new if data.empty?
  skip_ids_persistency&.save
  Result::ObjectList.new(data)
end

#action_async_show(async_id:) ⇒ Object

async sub-commands: individual handlers



969
970
971
972
973
974
975
976
977
978
979
980
# File 'lib/aspera/cli/plugins/node.rb', line 969

def action_async_show(async_id:, **)
  async_ids = api_node.read('async/list')['sync_ids']
  if async_id.eql?(SpecialValues::ALL)
    resp = api_node.create('async/summary', {'syncs' => async_ids})['sync_summaries']
    return Result::Empty.new if resp.empty?
    return Result::ObjectList.new(resp, fields: %w[snid name local_dir remote_dir])
  end
  Integer(async_id)
  resp = api_node.create('async/summary', {'syncs' => [async_id]})['sync_summaries']
  return Result::Empty.new if resp.empty?
  Result::SingleObject.new(resp.first)
end

#action_cat(path:) ⇒ Object



696
697
698
699
# File 'lib/aspera/cli/plugins/node.rb', line 696

def action_cat(path:, **)
  http = api_node.read("files/#{URI.encode_www_form_component(path)}/contents", ret: :resp)
  Result::Text.new(http.body)
end

#action_central_file_list(criteria:) ⇒ Object

central > file > list



1103
1104
1105
1106
1107
1108
1109
1110
1111
# File 'lib/aspera/cli/plugins/node.rb', line 1103

def action_central_file_list(criteria:, **)
  criteria ||= {}
  validation = central_validation
  criteria.deep_merge!({'validation' => validation}) unless validation.nil?
  resp = api_node.create('services/rest/transfers/v1/files', criteria)
  resp = JSON.parse(resp) if resp.is_a?(String)
  Log.dump(:resp, resp)
  Result::ObjectList.new(resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path])
end

#action_central_file_modify(file:) ⇒ Object

central > file > modify



1114
1115
1116
1117
1118
1119
1120
# File 'lib/aspera/cli/plugins/node.rb', line 1114

def action_central_file_modify(file:, **)
  file ||= {}
  validation = central_validation
  file.deep_merge!(validation) unless validation.nil?
  api_node.update('services/rest/transfers/v1/files', file)
  Result::Status.new('updated')
end

#action_central_session_list(criteria:) ⇒ Object

central > session > list



1094
1095
1096
1097
1098
1099
1100
# File 'lib/aspera/cli/plugins/node.rb', line 1094

def action_central_session_list(criteria:, **)
  criteria ||= {}
  validation = central_validation
  criteria.deep_merge!({'validation' => validation}) unless validation.nil?
  resp = api_node.create('services/rest/transfers/v1/sessions', criteria)
  Result::ObjectList.new(resp['session_info_result']['session_info'], fields: %w[session_uuid status transport direction bytes_transferred])
end

#action_delete(paths:) ⇒ Object

--- Handler methods (Gen3) ---



601
602
603
604
605
606
# File 'lib/aspera/cli/plugins/node.rb', line 601

def action_delete(paths:, **)
  # TODO: add query for recursive
  paths_to_delete = Array(paths)
  resp = api_node.create('files/delete', {paths: paths_to_delete.map { |i| {'path' => i.start_with?('/') ? i : "/#{i}"} }})
  cli_result_from_paths_response(resp, 'file deleted')
end

#action_download ⇒ Object



683
684
685
686
687
688
689
690
691
692
693
694
# File 'lib/aspera/cli/plugins/node.rb', line 683

def action_download(**)
  # empty transfer spec for authorization request
  request_transfer_spec = {}
  request_transfer_spec[:paths] = transfer.ts_source_paths
  # add fixed parameters if any (for COS)
  api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
  Api::Node.add_public_key(request_transfer_spec)
  setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
  transfer_spec = api_node.create('files/download_setup', setup_payload)['transfer_specs'].first['transfer_spec']
  Api::Node.add_private_key(transfer_spec)
  Runner.result_transfer(transfer.start(transfer_spec))
end

#action_health ⇒ Object



701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
# File 'lib/aspera/cli/plugins/node.rb', line 701

def action_health(**)
  nagios = Nagios.new
  begin
    info = api_node.read('info')
    nagios.add_ok('node api', 'accessible')
    nagios.check_time_offset(info['current_time'], 'node api')
    nagios.check_product_version('node api', 'entsrv', info['version'])
  rescue StandardError => e
    nagios.add_critical('node api', e.to_s)
  end
  begin
    api_node.call(
      operation:    'POST',
      subpath:      'services/soap/Transfer-201210',
      content_type: Mime::TEXT,
      body:         CENTRAL_SOAP_API_TEST,
      headers:      {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
      ret:          :resp
    ).body
    nagios.add_ok('central', 'accessible by node')
  rescue StandardError => e
    nagios.add_critical('central', e.to_s)
  end
  Result::ObjectList.new(nagios.status_list)
end

#action_mkdir(paths:) ⇒ Object



627
628
629
630
631
# File 'lib/aspera/cli/plugins/node.rb', line 627

def action_mkdir(paths:, **)
  paths = Array(paths)
  resp = api_node.create('files/create', {'paths' => paths.map { |i| {type: :directory, path: i} }})
  cli_result_from_paths_response(resp, 'folder created')
end

#action_mkfile(path:, contents:) ⇒ Object



638
639
640
641
642
# File 'lib/aspera/cli/plugins/node.rb', line 638

def action_mkfile(path:, contents:, **)
  contents64 = contents.nil? ? '' : Base64.strict_encode64(contents)
  resp = api_node.create('files/create', {'paths' => [{type: :file, path: path, contents: contents64}]})
  cli_result_from_paths_response(resp, 'file created')
end


633
634
635
636
# File 'lib/aspera/cli/plugins/node.rb', line 633

def action_mklink(target:, link_path:, **)
  resp = api_node.create('files/create', {'paths' => [{type: :symbolic_link, path: link_path, target: {path: target}}]})
  cli_result_from_paths_response(resp, 'link created')
end

#action_rename(folder:, source:, destination:) ⇒ Object



644
645
646
647
648
# File 'lib/aspera/cli/plugins/node.rb', line 644

def action_rename(folder:, source:, destination:, **)
  # TODO: multiple ?
  resp = api_node.create('files/rename', {'paths' => [{'path' => folder, 'source' => source, 'destination' => destination}]})
  cli_result_from_paths_response(resp, 'entry moved')
end

#action_search(path:) ⇒ Object



608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/aspera/cli/plugins/node.rb', line 608

def action_search(path:, **)
  parameters = {'path' => path}
  # not a URL query: merged into the POST body of files/search
  other_options = options.get_option(:query)
  parameters.merge!(other_options) unless other_options.nil?
  resp = api_node.create('files/search', parameters)
  return Result::Empty.new if resp['items'].empty?
  fields = resp['items'].first.keys.reject { |i| SEARCH_REMOVE_FIELDS.include?(i) }
  formatter.display_item_count(resp['item_count'], resp['total_count'])
  formatter.display_status("params: #{resp['parameters'].keys.map { |k| "#{k}:#{resp['parameters'][k]}" }.join(',')}")
  Result::ObjectList.new(resp['items'], fields: fields)
end

#action_simulator(parameters: {}) ⇒ Object



1134
1135
1136
1137
1138
1139
1140
1141
1142
# File 'lib/aspera/cli/plugins/node.rb', line 1134

def action_simulator(parameters: {}, **)
  require 'aspera/node_simulator'
  parameters = parameters.symbolize_keys
  uri = URI.parse(parameters.delete(:url) { WebServerSimple::DEFAULT_URL })
  server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
  server.mount(uri.path, NodeSimulatorServlet, parameters.except(*WebServerSimple::PARAMS), NodeSimulator.new)
  server.start
  return Result::Status.new('Simulator terminated')
end

#action_space(paths:) ⇒ Object



621
622
623
624
625
# File 'lib/aspera/cli/plugins/node.rb', line 621

def action_space(paths:, **)
  paths = Array(paths)
  resp = api_node.create('space', {'paths' => paths.map { |i| {path: i} }})
  Result::ObjectList.new(resp['paths'])
end

#action_telemetry(parameters: {}) ⇒ Object



1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
# File 'lib/aspera/cli/plugins/node.rb', line 1144

def action_telemetry(parameters: {}, **)
  parameters = parameters.symbolize_keys
  %i[url key].each do |psym|
    Aspera.assert(parameters.key?(psym), type: Cli::BadArgument) { "Missing parameter: #{psym}" }
  end
  require 'socket'
  parameters[:interval] = 10 unless parameters.key?(:interval)
  parameters[:hostname] = Socket.gethostname unless parameters.key?(:hostname)
  interval = parameters[:interval].to_f
  Aspera.assert(interval >= 0, type: Cli::BadArgument) { 'Interval must be a positive number in seconds, or 0 for single shot' }
  otel_api = Rest::Client.new(
    base_url: "#{parameters[:url]}/v1",
    headers: {
      # 'Authorization'  => "apiToken #{parameters[:key]}",
      'x-instana-key'  => parameters[:key],
      'x-instana-host' => parameters[:hostname]
    }
  )
  datapoint = {
    attributes:   [
      {
        key:   'server.name',
        value: {
          stringValue: 'HSTS1'
        }
      }
    ],
    asInt:        nil,
    timeUnixNano: nil
  }
  # https://opentelemetry.io/docs/specs/otel/metrics/data-model/#gauge
  metrics = {
    resourceMetrics: [
      {
        resource:     {
          attributes: [
            {
              key:   'service.name',
              value: {
                stringValue: 'IBMAspera'
              }
            }
          ]
        },
        scopeMetrics: [
          {
            metrics: [
              {
                name:        'active.transfers',
                description: 'Number of active transfers',
                unit:        '1',
                gauge:       {
                  dataPoints: [
                    datapoint
                  ]
                }
              }
            ]
          }
        ]
      }
    ]
  }
  loop do
    timestamp = Time.now
    transfers_data = api_node.read_with_paging('ops/transfers', {active_only: true})
    datapoint[:asInt] = transfers_data.length
    datapoint[:timeUnixNano] = timestamp.to_i * 1_000_000_000 + timestamp.nsec
    Log.log.info("#{datapoint[:asInt]} active transfers")
    # https://www.ibm.com/docs/en/instana-observability/current?topic=instana-backend
    otel_api.create('metrics', metrics)
    break if interval.eql?(0.0)
    sleep([0.0, interval - (Time.now - timestamp)].max)
  end
end

#action_transfer_bandwidth_average ⇒ Object



1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
# File 'lib/aspera/cli/plugins/node.rb', line 1057

def action_transfer_bandwidth_average(**)
  transfers_data = api_node.read('ops/transfers', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers')))
  bandwidth_period = {}
  dir_info = %i[avg_kbps sessions].freeze
  transfers_data.each do |t|
    next if t['avg_rate_kbps'].zero?
    bandwidth_period[t['start_time_usec']] = 0
    bandwidth_period[t['end_time_usec']] = 0
  end
  result = []
  all_dates = bandwidth_period.keys.sort
  all_dates.each_with_index do |start_date, index|
    end_date = all_dates[index + 1]
    break if end_date.nil?
    period_bandwidth = Transfer::Spec::DIRECTION_ENUM_VALUES.map(&:to_sym).to_h do |dir|
      [dir, dir_info.to_h { |k2| [k2, 0] }]
    end
    transfers_data.each do |t|
      next if t['avg_rate_kbps'].zero?
      next if t['start_time_usec'] >= end_date || t['end_time_usec'] <= start_date
      info = period_bandwidth[t['start_spec']['direction'].to_sym]
      info[:avg_kbps] += t['avg_rate_kbps']
      info[:sessions] += 1
    end
    next if Transfer::Spec::DIRECTION_ENUM_VALUES.map(&:to_sym).all? { |dir| period_bandwidth[dir][:sessions].zero? }
    result.push({start: Time.at(start_date / 1_000_000), end: Time.at(end_date / 1_000_000)}.merge(period_bandwidth))
  end
  Result::ObjectList.new(result)
end

#action_transfer_list ⇒ Object

transfer sub-commands



1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
# File 'lib/aspera/cli/plugins/node.rb', line 1024

def action_transfer_list(**)
  transfer_filter = query_read_delete(default: {}, schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers'))
  iteration_persistency = nil
  if options.get_option(:once_only, mandatory: true)
    iteration_persistency = PersistencyActionOnce.new(
      manager: persistency,
      data:    [],
      id:      IdGenerator.from_list('node_transfers', options.get_option(:url, mandatory: true), options.get_option(:username, mandatory: true))
    )
    if transfer_filter.delete('reset')
      iteration_persistency.data.clear
      iteration_persistency.save
      return Result::Status.new('Persistency reset')
    end
  else
    Aspera.assert(!transfer_filter.key?('reset'), 'reset only with once_only', type: Cli::BadArgument)
  end
  transfers_data = api_node.read_with_paging('ops/transfers', transfer_filter, iteration: iteration_persistency&.data)
  iteration_persistency&.save
  Result::ObjectList.new(transfers_data, fields: %w[id status start_spec.direction start_spec.remote_user start_spec.remote_host start_spec.destination_path])
end

#action_transfer_sessions ⇒ Object



1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
# File 'lib/aspera/cli/plugins/node.rb', line 1046

def action_transfer_sessions(**)
  transfers_data = api_node.read('ops/transfers', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers')))
  sessions = transfers_data.flat_map { |t| t['sessions'] }
  sessions.each do |session|
    SESSION_TIME_FIELDS.each do |what|
      session["#{what}_time"] = session["#{what}_time_usec"] ? Time.at(session["#{what}_time_usec"] / 1_000_000.0).utc.iso8601(0) : nil
    end
  end
  Result::ObjectList.new(sessions, fields: %w[id status start_time end_time target_rate_kbps])
end

#action_upload ⇒ Object



669
670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/aspera/cli/plugins/node.rb', line 669

def action_upload(**)
  # empty transfer spec for authorization request
  request_transfer_spec = {}
  request_transfer_spec[:paths] = [{destination: transfer.destination_folder(Transfer::Spec::DIRECTION_SEND)}]
  # add fixed parameters if any (for COS)
  api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
  Api::Node.add_public_key(request_transfer_spec)
  setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
  transfer_spec = api_node.create('files/upload_setup', setup_payload)['transfer_specs'].first['transfer_spec']
  Api::Node.add_private_key(transfer_spec)
  transfer_spec.delete('paths')
  Runner.result_transfer(transfer.start(transfer_spec))
end

#api_node ⇒ Api::Node, Rest::Client

Node API: provided by the host plugin, or built from CLI options on first use.

Returns:



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/aspera/cli/plugins/node.rb', line 178

def api_node
  @api_node ||=
    if OAuth::Factory.bearer_auth?(options.get_option(:password, mandatory: true))
      # info is provided like node_info of aoc
      Api::Node.new(
        base_url: options.get_option(:url, mandatory: true),
        headers:  Api::Node.bearer_headers(options.get_option(:password, mandatory: true))
      )
    else
      # this is normal case
      Api::Node.new(
        base_url: options.get_option(:url, mandatory: true),
        auth:     {
          type:     :basic,
          username: options.get_option(:username, mandatory: true),
          password: options.get_option(:password, mandatory: true)
        }
      )
    end
end

#apifid_from_path(top_file_id, path) ⇒ Object

Resolve a NodeFileId from a path argument. Supports %id:<file_id> syntax (returns NodeFileId for that id) or a plain path.

Parameters:

  • root file id for path resolution

  • plain path or %id:<file_id> selector



287
288
289
290
291
292
293
294
# File 'lib/aspera/cli/plugins/node.rb', line 287

def apifid_from_path(top_file_id, path)
  if (m = Parser.percent_selector(path))
    Aspera.assert_values(m[:field], ['id'], type: BadArgument) { 'file id' }
    val = m[:value]
    return Api::NodeFileId.new(api_node, val.nil? || val.empty? ? top_file_id : val)
  end
  api_node.resolve_api_fid(top_file_id, path)
end

#async_lookup(field, value) ⇒ Integer

Search /async by name

Parameters:

  • name of the field to search

  • value of the field to search

Returns:

  • id of the sync

Raises:

  • if no such sync, or not by name



301
302
303
304
305
306
307
308
# File 'lib/aspera/cli/plugins/node.rb', line 301

def async_lookup(field, value)
  Aspera.assert_values(field, ['name'], type: Cli::BadArgument) { 'search field' }
  async_ids = api_node.read('async/list')['sync_ids']
  summaries = api_node.create('async/summary', {'syncs' => async_ids})['sync_summaries']
  selected = summaries.find { |s| s['name'].eql?(value) }
  raise Cli::BadIdentifier.new('sync', value, field: field) if selected.nil?
  return selected['snid']
end

#browse_gen3(path) ⇒ Object

Gen3 API

Parameters:

  • starting path



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/aspera/cli/plugins/node.rb', line 201

def browse_gen3(path)
  folders_to_process = path
  folders_to_process = [folders_to_process]
  # not a URL query: merged into the POST body of files/browse
  query = options.get_option(:query) || {}
  # special parameter: max number of entries in result
  max_items = query.delete(Rest::List::MAX_ITEMS)
  # special parameter: recursive browsing
  recursive = query.delete('recursive')
  # special parameter: only return one entry for the path, even if folder
  only_path = query.delete('self')
  # allow user to specify a single call, and not recursive
  single_call = query.key?('skip')
  # API default is 100, so use 1000 for default
  query['count'] ||= 1000
  Aspera.assert(!(recursive && single_call), type: Cli::BadArgument) { 'options `recursive` and `skip` cannot be used together' }
  all_items = []
  until folders_to_process.empty?
    path = folders_to_process.shift
    query['path'] = path
    offset = 0
    total_count = nil
    loop do
      # example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
      response = api_node.create('files/browse', query)
      # 'file','symbolic_link'
      return Result::SingleObject.new(response['self']) if !Node.gen3_entry_folder?(response['self']) || only_path
      items = response['items']
      total_count ||= response['total_count']
      all_items.concat(items)
      if single_call
        formatter.display_item_count(response['item_count'], total_count)
        break
      end
      folders_to_process.concat(items.select { |i| Node.gen3_entry_folder?(i) }.map { |i| i['path'] }) if recursive
      if !max_items.nil? && (all_items.count >= max_items)
        all_items = all_items.slice(0, max_items) if all_items.count > max_items
        break
      end
      break if all_items.count >= total_count
      offset += items.count
      query['skip'] = offset
      Rest::Parameters.instance.spinner_cb.call(all_items.count)
    end
    query.delete('skip')
  end
  return Result::ObjectList.new(all_items)
ensure
  Rest::Parameters.instance.spinner_cb.call(action: :success)
end

#central_validation ⇒ Object

central: shared helper



1088
1089
1090
1091
# File 'lib/aspera/cli/plugins/node.rb', line 1088

def central_validation
  validator_id = options.get_option(:validator)
  validator_id ? {'validator_id' => validator_id} : nil
end

#gen4_apifid_info(do_root_file_id, path) ⇒ Object

access_keys > do > node_info / bearer_token_node — shared helper builds the result hash



851
852
853
854
855
856
857
858
859
860
861
862
863
864
# File 'lib/aspera/cli/plugins/node.rb', line 851

def gen4_apifid_info(do_root_file_id, path)
  apifid = apifid_from_path(do_root_file_id, path)
  result = {url: apifid.node_api.base_url, root_id: apifid.file_id}
  case apifid.node_api.auth_params[:type]
  when :basic
    result[:username] = apifid.node_api.auth_params[:username]
    result[:password] = apifid.node_api.auth_params[:password]
  when :oauth2
    result[:username] = apifid.node_api.params[:headers][Api::Node::HEADER_X_ASPERA_ACCESS_KEY]
    result[:password] = apifid.node_api.oauth.authorization
  else Aspera.error_unexpected_value(apifid.node_api.auth_params[:type]) { 'Node API Auth type' }
  end
  [apifid, result]
end

#gen4_mk_resolve(top_file_id, path) ⇒ Array(NodeFileId, Hash)

access_keys > do > mkdir / mklink / mkfile — shared helper: resolve parent folder, build payload from query option, optionally check for name collision.

Parameters:

  • full path (folder/name)

Returns:

  • parent apifid and payload with :name set



883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
# File 'lib/aspera/cli/plugins/node.rb', line 883

def gen4_mk_resolve(top_file_id, path)
  containing_folder_path, new_item = Api::Node.split_folder(path)
  apifid = api_node.resolve_api_fid(top_file_id, containing_folder_path, true)
  # not a URL query: special keys `check` and `target`, rest merged into the POST body
  query = options.get_option(:query)
  check_exists = true
  payload = {name: new_item}
  if query
    check_exists = !query.delete('check').eql?(false)
    target = query.delete('target')
    if target
      target_apifid = api_node.resolve_api_fid(top_file_id, target, true)
      payload[:target_id] = target_apifid.file_id
    end
    payload.merge!(query.symbolize_keys)
  end
  if check_exists
    folder_content = apifid.node_api.read("files/#{apifid.file_id}/files")
    link_name = ".#{new_item}.asp-lnk"
    found = folder_content.find { |i| i['name'].eql?(new_item) || i['name'].eql?(link_name) }
    Aspera.assert(!found, type: Cli::Error) { "A #{found['type']} already exists with name #{new_item}" }
  end
  [apifid, payload]
end

#lookup_access_key_self_id(field, value) ⇒ String

Lookup for access_keys: only supports %id:self selector.

Parameters:

  • must be 'id'

  • must be 'self'

Returns:

  • the resolved access key id



314
315
316
317
# File 'lib/aspera/cli/plugins/node.rb', line 314

def lookup_access_key_self_id(field, value, **)
  Aspera.assert(field.eql?('id') && value.eql?('self'), type: BadArgument) { 'only selector: %id:self' }
  api_node.read('access_keys/self')['id']
end

#setup_access_key_do(access_key_id:) ⇒ Hash

access_keys > do - setup: resolve access key and root file id

Returns:

  • context hash containing :do_root_file_id



735
736
737
738
739
740
741
742
743
744
745
746
747
748
# File 'lib/aspera/cli/plugins/node.rb', line 735

def setup_access_key_do(access_key_id:, **)
  @do_root_file_id = options.get_option(:root_id)
  if @do_root_file_id.nil?
    ak_info = api_node.read("access_keys/#{access_key_id}")
    ak_secret = context.secret_finder.lookup(url: api_node.base_url, username: ak_info['id'])
    if !access_key_id.eql?('self')
      Aspera.assert(ak_secret, type: Cli::MissingArgument) { "Please provide secret for #{ak_info['id']} using option: secret or by setting a preset for #{ak_info['id']}@#{api_node.base_url}." }
      api_node.auth_params[:username] = ak_info['id']
      api_node.auth_params[:password] = ak_secret
    end
    @do_root_file_id = ak_info['root_file_id']
  end
  {do_root_file_id: @do_root_file_id}
end

#setup_access_key_do_permission(do_root_file_id:, path:) ⇒ Hash

access_keys > do > permission - setup: resolve apifid from the path argument of the node do_root_file_id: comes from ctx (setup_access_key_do, or seed of a mount)

Returns:

  • context hash containing :apifid



753
754
755
# File 'lib/aspera/cli/plugins/node.rb', line 753

def setup_access_key_do_permission(do_root_file_id:, path:, **)
  {apifid: apifid_from_path(do_root_file_id, path)}
end

#setup_watch_folder ⇒ Object

watch_folder setup: inject required API header (avoids "Unable to convert 2016_09_14 configuration")



728
729
730
731
# File 'lib/aspera/cli/plugins/node.rb', line 728

def setup_watch_folder(**)
  api_node.headers['X-aspera-WF-version'] = '2017_10_23'
  {}
end

#ssync_lookup(field, value) ⇒ Integer

Search /asyncs by name

Parameters:

  • name of the field to search

  • value of the field to search

Returns:

  • id of the sync

Raises:

  • if no such sync, or not by name



324
325
326
327
328
329
330
331
332
# File 'lib/aspera/cli/plugins/node.rb', line 324

def ssync_lookup(field, value)
  Aspera.assert_values(field, ['name'], type: Cli::BadArgument) { 'search field' }
  api_node.read('asyncs')['ids'].each do |id|
    sync_info = api_node.read("asyncs/#{id}")['configuration']
    # name is unique, so we can return
    return id if sync_info[field].eql?(value)
  end
  raise Cli::BadIdentifier.new('ssync', value, field: field)
end

#sync_gen3_block ⇒ Object

Obtains a transfer spec via the Node API for the given direction/folders.



651
652
653
654
655
656
657
658
659
660
661
662
663
# File 'lib/aspera/cli/plugins/node.rb', line 651

def sync_gen3_block
  lambda do |direction, local_path, remote_path|
    request_transfer_spec = sync_spec_request(direction, local_path, remote_path)
    api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
    transfer_spec = api_node.create(
      'files/sync_setup',
      {transfer_requests: [{transfer_request: request_transfer_spec}]}
    )['transfer_specs'].first['transfer_spec']
    transfer_spec.delete_if { |_k, v| v.nil? }
    Log.dump(:ts, transfer_spec)
    transfer_spec
  end
end

#sync_gen4_block(do_root_file_id) ⇒ Object

Shared Gen4 sync block: obtains a transfer spec via the Gen4 API for the given direction/remote_path.



818
819
820
821
822
823
824
# File 'lib/aspera/cli/plugins/node.rb', line 818

def sync_gen4_block(do_root_file_id)
  lambda do |direction, _local_path, remote_path|
    ts_direction = direction.eql?(:pull) ? Transfer::Spec::DIRECTION_RECEIVE : Transfer::Spec::DIRECTION_SEND
    apifid = api_node.resolve_api_fid(do_root_file_id, remote_path)
    apifid.node_api.transfer_spec_gen4(apifid.file_id, ts_direction)
  end
end

#sync_spec_request(sync_direction, local_path, remote_path) ⇒ Object

Create async transfer spec request from direction and folders

Parameters:

  • one of push pull bidi

  • local folder to sync

  • remote folder to sync



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/aspera/cli/plugins/node.rb', line 256

def sync_spec_request(sync_direction, local_path, remote_path)
  case sync_direction
  when :push then {
    type:  :sync_upload,
    paths: [{
      source:      local_path,
      destination: remote_path
    }]
  }
  when :pull then {
    type:  :sync_download,
    paths: [{
      source:      remote_path,
      destination: local_path
    }]
  }
  when :bidi then {
    type:  :sync,
    paths: [{
      source:      local_path,
      destination: remote_path
    }]
  }
  else Aspera.error_unexpected_value(sync_direction)
  end
end

#v3_node_plugin(do_root_file_id:) ⇒ Node

access_keys > do > v3 - mount target: Node plugin on the node hosting the root file

Returns:



846
847
848
# File 'lib/aspera/cli/plugins/node.rb', line 846

def v3_node_plugin(do_root_file_id:, **)
  Node.new(context: context, api: api_node.resolve_api_fid(do_root_file_id, '').node_api)
end

#wizard(wizard, app_url) ⇒ Hash

Returns :preset_value, :test_args.

Parameters:

  • The wizard object

  • Tested URL

Returns:

  • :preset_value, :test_args



86
87
88
89
90
91
92
93
94
95
# File 'lib/aspera/cli/plugins/node.rb', line 86

def wizard(wizard, app_url)
  return {
    preset_value: {
      url:      app_url,
      username: options.get_option(:username, mandatory: true),
      password: options.get_option(:password, mandatory: true)
    },
    test_args:    'info'
  }
end