class Asperalm::Cli::Plugins::Node

Constants

ACTIONS
COMMON_ACTIONS
SAMPLE_SOAP_CALL
SIMPLE_ACTIONS

Public Class Methods

new(env) click to toggle source
Calls superclass method Asperalm::Cli::BasicAuthPlugin::new
# File lib/asperalm/cli/plugins/node.rb, line 13
def initialize(env)
  super(env)
  # this is added to some requests , for instance to add tags
  @add_request_param = env[:add_request_param] || {}
  unless env[:skip_basic_auth_options]
    self.options.add_opt_simple(:validator,"identifier of validator (optional for central)")
    self.options.add_opt_simple(:asperabrowserurl,"URL for simple aspera web ui")
    self.options.add_opt_simple(:name,"sync name")
    self.options.add_opt_list(:token,[:aspera,:basic,:auto],'package box')
    self.options.set_option(:asperabrowserurl,'https://asperabrowser.mybluemix.net')
    self.options.set_option(:token,:aspera)
    self.options.parse_options!
  end
  return if env[:man_only]
  if env.has_key?(:node_api)
    @api_node=env[:node_api]
  else
    @api_node=basic_auth_api unless env[:man_only]
  end
end

Public Instance Methods

c_result_remove_prefix_path(result,column,path_prefix) click to toggle source

reduce the path from a result on given named column

# File lib/asperalm/cli/plugins/node.rb, line 53
def c_result_remove_prefix_path(result,column,path_prefix)
  if !path_prefix.nil?
    case result[:type]
    when :object_list
      result[:data].each do |item|
        item[column].replace(item[column][path_prefix.length..-1]) if item[column].start_with?(path_prefix)
      end
    when :single_object
      item=result[:data]
      item[column].replace(item[column][path_prefix.length..-1]) if item[column].start_with?(path_prefix)
    end
  end
  return result
end
c_result_translate_rem_prefix(resp,type,success_msg,path_prefix) click to toggle source

translates paths results into CLI result, and removes prefix

# File lib/asperalm/cli/plugins/node.rb, line 69
def c_result_translate_rem_prefix(resp,type,success_msg,path_prefix)
  resres={:data=>[],:type=>:object_list,:fields=>[type,'result']}
  JSON.parse(resp[:http].body)['paths'].each do |p|
    result=success_msg
    if p.has_key?('error')
      Log.log.error("#{p['error']['user_message']} : #{p['path']}")
      result="ERROR: "+p['error']['user_message']
    end
    resres[:data].push({type=>p['path'],'result'=>result})
  end
  return c_result_remove_prefix_path(resres,type,path_prefix)
end
c_textify_bool_list_result(list,name_list) click to toggle source

key/value is defined in main in hash_table

# File lib/asperalm/cli/plugins/node.rb, line 39
def c_textify_bool_list_result(list,name_list)
  list.each_index do |i|
    if name_list.include?(list[i]['key'])
      list[i]['value'].each do |item|
        list.push({'key'=>item['name'],'value'=>item['value']})
      end
      list.delete_at(i)
      # continue at same index because we delete current one
      redo
    end
  end
end
c_textify_browse(table_data) click to toggle source
# File lib/asperalm/cli/plugins/node.rb, line 34
def c_textify_browse(table_data)
  return table_data.map {|i| i['permissions']=i['permissions'].map { |x| x['name'] }.join(','); i }
end
execute_action(command=nil,prefix_path=nil) click to toggle source
# File lib/asperalm/cli/plugins/node.rb, line 292
def execute_action(command=nil,prefix_path=nil)
  command||=self.options.get_next_command(ACTIONS)
  case command
  when *COMMON_ACTIONS; return execute_simple_common(command,prefix_path)
  when :async; return execute_async()
  when :stream
    command=self.options.get_next_command([ :list, :create, :show, :modify, :cancel ])
    case command
    when :list
      resp=@api_node.read('ops/transfers',self.options.get_option(:value,:optional))
      return { :type => :object_list, :data => resp[:data], :fields=>['id','status']  } # TODO
    when :create
      resp=@api_node.create('streams',self.options.get_option(:value,:mandatory))
      return { :type => :single_object, :data => resp[:data] }
    when :show
      trid=self.options.get_next_argument("transfer id")
      resp=@api_node.read('ops/transfers/'+trid)
      return { :type=>:other_struct, :data => resp[:data] }
    when :modify
      trid=self.options.get_next_argument("transfer id")
      resp=@api_node.update('streams/'+trid,self.options.get_option(:value,:mandatory))
      return { :type=>:other_struct, :data => resp[:data] }
    when :cancel
      trid=self.options.get_next_argument("transfer id")
      resp=@api_node.cancel('streams/'+trid)
      return { :type=>:other_struct, :data => resp[:data] }
    else
      raise "error"
    end
  when :transfer
    command=self.options.get_next_command([ :list, :cancel, :show ])
    res_class_path='ops/transfers'
    if [:cancel, :show].include?(command)
      one_res_id=self.options.get_option(:id,:mandatory)
      one_res_path="#{res_class_path}/#{one_res_id}"
    end
    case command
    when :list
      # could use ? :subpath=>'transfers'
      resp=@api_node.read(res_class_path,self.options.get_option(:value,:optional))
      return { :type => :object_list, :data => resp[:data], :fields=>['id','status','start_spec.direction','start_spec.remote_user','start_spec.remote_host','start_spec.destination_path']}
    when :cancel
      resp=@api_node.cancel(one_res_path)
      return { :type=>:other_struct, :data => resp[:data] }
    when :show
      resp=@api_node.read(one_res_path)
      return { :type=>:other_struct, :data => resp[:data] }
    else
      raise "error"
    end
  when :access_key
    return self.entity_action(@api_node,'access_keys',['id','root_file_id','storage'],:id,'self')
  when :service
    command=self.options.get_next_command([ :list, :create, :delete])
    if [:delete].include?(command)
      svcid=self.options.get_option(:id,:mandatory)
    end
    case command
    when :list
      resp=@api_node.read('rund/services')
      return { :type=>:object_list, :data => resp[:data]["services"] }
    when :create
      # @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
      params=self.options.get_next_argument("Run creation data (structure)")
      resp=@api_node.create('rund/services',params)
      return Main.result_status("#{resp[:data]['id']} created")
    when :delete
      resp=@api_node.delete("rund/services/#{svcid}")
      return Main.result_status("#{svcid} deleted")
    end
  when :watch_folder
    res_class_path='v3/watchfolders'
    #return entity_action(@api_node,'v3/watchfolders',nil,:id)
    command=self.options.get_next_command([ :create, :list, :show, :modify, :delete, :state])
    if [:show,:modify,:delete,:state].include?(command)
      one_res_id=self.options.get_option(:id,:mandatory)
      one_res_path="#{res_class_path}/#{one_res_id}"
    end
    # hum, to avoid: Unable to convert 2016_09_14 configuration
    @api_node.params[:headers]||={}
    @api_node.params[:headers]['X-aspera-WF-version']='2017_10_23'
    case command
    when :create
      resp=@api_node.create(res_class_path,self.options.get_option(:value,:mandatory))
      return Main.result_status("#{resp[:data]['id']} created")
    when :list
      resp=@api_node.read(res_class_path,self.options.get_option(:value,:optional))
      return { :type=>:value_list, :data => resp[:data]['ids'], :name=>'id' }
    when :show
      return {:type=>:single_object, :data=>@api_node.read(one_res_path)[:data]}
    when :modify
      @api_node.update(one_res_path,self.options.get_option(:value,:mandatory))
      return Main.result_status("#{one_res_id} updated")
    when :delete
      @api_node.delete(one_res_path)
      return Main.result_status("#{one_res_id} deleted")
    when :state
      return { :type=>:single_object, :data => @api_node.read("#{one_res_path}/state")[:data] }
    end
  when :central
    command=self.options.get_next_command([ :session,:file])
    validator_id=self.options.get_option(:validator)
    validation={"validator_id"=>validator_id} unless validator_id.nil?
    request_data=self.options.get_option(:value,:optional)
    request_data||={}
    case command
    when :session
      command=self.options.get_next_command([ :list])
      case command
      when :list
        request_data.deep_merge!({"validation"=>validation}) unless validation.nil?
        resp=@api_node.create('services/rest/transfers/v1/sessions',request_data)
        return {:type=>:object_list,:data=>resp[:data]["session_info_result"]["session_info"],:fields=>["session_uuid","status","transport","direction","bytes_transferred"]}
      end
    when :file
      command=self.options.get_next_command([ :list, :modify])
      case command
      when :list
        request_data.deep_merge!({"validation"=>validation}) unless validation.nil?
        resp=@api_node.create('services/rest/transfers/v1/files',request_data)
        return {:type=>:object_list,:data=>resp[:data]["file_transfer_info_result"]["file_transfer_info"],:fields=>["session_uuid","file_id","status","path"]}
      when :modify
        request_data.deep_merge!(validation) unless validation.nil?
        @api_node.update('services/rest/transfers/v1/files',request_data)
        return Main.result_status('updated')
      end
    end
  when :asperabrowser
    browse_params={
      'nodeUser' => self.options.get_option(:username,:mandatory),
      'nodePW'   => self.options.get_option(:password,:mandatory),
      'nodeURL'  => self.options.get_option(:url,:mandatory)
    }
    # encode parameters so that it looks good in url
    encoded_params=Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
    OpenApplication.instance.uri(self.options.get_option(:asperabrowserurl)+'?goto='+encoded_params)
    return Main.result_status('done')
  when :basic_token
    return Main.result_status("Basic "+Base64.strict_encode64("#{self.options.get_option(:username,:mandatory)}:#{self.options.get_option(:password,:mandatory)}"))
  end # case command
  raise "ERROR: shall not reach this line"
end
execute_async() click to toggle source
# File lib/asperalm/cli/plugins/node.rb, line 215
def execute_async
  command=self.options.get_next_command([:list,:delete,:files,:show,:counters,:bandwidth])
  unless command.eql?(:list)
    asyncname=self.options.get_option(:name,:optional)
    if asyncname.nil?
      asyncid=self.options.get_option(:id,:mandatory)
      if asyncid.eql?('ALL') and [:show,:delete].include?(command)
        asyncids=@api_node.read('async/list')[:data]['sync_ids']
      else
        Integer(asyncid) # must be integer
        asyncids=[asyncid]
      end
    else
      asyncids=@api_node.read('async/list')[:data]['sync_ids']
      summaries=@api_node.create('async/summary',{'syncs' => asyncids})[:data]['sync_summaries']
      selected=summaries.select{|s|s['name'].eql?(asyncname)}.first
      raise "no such sync: #{asyncname}" if selected.nil?
      asyncid=selected['snid']
      asyncids=[asyncid]
    end
    pdata={'syncs' => asyncids}
  end
  case command
  when :list
    resp=@api_node.read('async/list')[:data]['sync_ids']
    return { :type => :value_list, :data => resp, :name=>'id'  }
  when :show
    resp=@api_node.create('async/summary',pdata)[:data]['sync_summaries']
    return Main.result_empty if resp.empty?
    if asyncid.eql?('ALL')
      return { :type => :object_list, :data => resp, :fields => ['snid','name','local_dir','remote_dir'] }
    else
      return { :type => :single_object, :data => resp.first }
    end
  when :delete
    resp=@api_node.create('async/delete',pdata)[:data]
    return { :type => :single_object, :data => resp, :name=>'id'  }
  when :bandwidth
    pdata['seconds']=100  # TODO: as parameter with --value
    resp=@api_node.create('async/bandwidth',pdata)[:data]
    data=resp['bandwidth_data']
    return Main.result_empty if data.empty?
    data=data.first[asyncid]['data']
    return { :type => :object_list, :data => data, :name=>'id'  }
  when :files
    # count int
    # filename str
    # skip int
    # status int
    filter=self.options.get_option(:value,:optional)
    pdata.merge!(filter) unless filter.nil?
    resp=@api_node.create('async/files',pdata)[:data]
    data=resp['sync_files']
    data=data.first[asyncid] unless data.empty?
    iteration_data=[]
    skip_ids_persistency=nil
    if self.options.get_option(:once_only,:mandatory)
      skip_ids_persistency=PersistencyFile.new(
      data: iteration_data,
      ids:  ['sync_files',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),asyncid])
      unless iteration_data.first.nil?
        data.select!{|l| l['fnid'].to_i>iteration_data.first}
      end
      iteration_data[0]=data.last['fnid'].to_i unless data.empty?
    end
    return Main.result_empty if data.empty?
    skip_ids_persistency.save unless skip_ids_persistency.nil?
    return { :type => :object_list, :data => data, :name=>'id'  }
  when :counters
    resp=@api_node.create('async/counters',pdata)[:data]["sync_counters"].first[asyncid].last
    return Main.result_empty if resp.nil?
    return { :type => :single_object, :data => resp }
  end
end
execute_simple_common(command,prefix_path) click to toggle source

common API to node and Shares prefix_path is used to list remote sources in Faspex

# File lib/asperalm/cli/plugins/node.rb, line 97
def execute_simple_common(command,prefix_path)
  case command
  when :nagios_check
    nagios=Nagios.new
    begin
      info=@api_node.read('info')[:data]
      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 => e
      nagios.add_critical('node api',e.to_s)
    end
    begin
      @api_node.call({:operation=>'POST',:subpath=>'services/soap/Transfer-201210',:headers=>{'Content-Type'=>'text/xml;charset=UTF-8','SOAPAction'=>'FASPSessionNET-200911#GetSessionInfo'},:text_body_params=>SAMPLE_SOAP_CALL})[:http].body
      nagios.add_ok('central','accessible by node')
    rescue => e
      nagios.add_critical('central',e.to_s)
    end
    return nagios.result
  when :events
    events=@api_node.read('events',self.options.get_option(:value,:optional))[:data]
    return { :type=>:object_list, :data => events}
  when :info
    node_info=@api_node.read('info')[:data]
    return { :type=>:single_object, :data => node_info, :textify => lambda { |table_data| c_textify_bool_list_result(table_data,['capabilities','settings'])}}
  when :license # requires: asnodeadmin -mu <node user> --acl-add=internal --internal
    node_license=@api_node.read('license')[:data]
    if node_license['failure'].is_a?(String) and node_license['failure'].include?('ACL')
      Log.log.error("server must have: asnodeadmin -mu <node user> --acl-add=internal --internal")
    end
    return { :type=>:single_object, :data => node_license}
  when :delete
    paths_to_delete = get_next_arg_add_prefix(prefix_path,"file list",:multiple)
    resp=@api_node.create('files/delete',{:paths=>paths_to_delete.map{|i| {'path'=>i.start_with?('/') ? i : '/'+i} }})
    return c_result_translate_rem_prefix(resp,'file','deleted',prefix_path)
  when :search
    search_root = get_next_arg_add_prefix(prefix_path,"search root")
    parameters={'path'=>search_root}
    other_options=self.options.get_option(:value,:optional)
    parameters.merge!(other_options) unless other_options.nil?
    resp=@api_node.create('files/search',parameters)
    result={ :type=>:object_list, :data => resp[:data]['items']}
    return Main.result_empty if result[:data].empty?
    result[:fields]=result[:data].first.keys.select{|i|!['basename','permissions'].include?(i)}
    self.format.display_status("Items: #{resp[:data]['item_count']}/#{resp[:data]['total_count']}")
    self.format.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
    return c_result_remove_prefix_path(result,'path',prefix_path)
  when :space
    # TODO: could be a list of path
    path_list=get_next_arg_add_prefix(prefix_path,"folder path or ext.val. list")
    path_list=[path_list] unless path_list.is_a?(Array)
    resp=@api_node.create('space',{ "paths" => path_list.map {|i| {:path=>i} } } )
    result={:data=>resp[:data]['paths'],:type=>:object_list}
    #return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
    return c_result_remove_prefix_path(result,'path',prefix_path)
  when :mkdir
    path_list=get_next_arg_add_prefix(prefix_path,"folder path or ext.val. list")
    path_list=[path_list] unless path_list.is_a?(Array)
    #TODO
    #resp=@api_node.create('space',{ "paths" => path_list.map {|i| {:type=>:directory,:path=>i} } } )
    resp=@api_node.create('files/create',{ "paths" => [{ :type => :directory, :path => path_list } ] } )
    return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
  when :mklink
    target=get_next_arg_add_prefix(prefix_path,"target")
    path_list=get_next_arg_add_prefix(prefix_path,"link path")
    resp=@api_node.create('files/create',{ "paths" => [{ :type => :symbolic_link, :path => path_list, :target => {:path => target} } ] } )
    return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
  when :mkfile
    path_list=get_next_arg_add_prefix(prefix_path,"file path")
    contents64=Base64.strict_encode64(self.options.get_next_argument("contents"))
    resp=@api_node.create('files/create',{ "paths" => [{ :type => :file, :path => path_list, :contents => contents64 } ] } )
    return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
  when :rename
    path_base=get_next_arg_add_prefix(prefix_path,"path_base")
    path_src=get_next_arg_add_prefix(prefix_path,"path_src")
    path_dst=get_next_arg_add_prefix(prefix_path,"path_dst")
    resp=@api_node.create('files/rename',{ "paths" => [{ "path" => path_base, "source" => path_src, "destination" => path_dst } ] } )
    return c_result_translate_rem_prefix(resp,'entry','moved',prefix_path)
  when :browse
    thepath=get_next_arg_add_prefix(prefix_path,"path")
    query={ :path => thepath}
    additional_query=self.options.get_option(:query,:optional)
    query.merge!(additional_query) unless additional_query.nil?
    send_result=@api_node.create('files/browse', query)[:data]
    #example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
    # if there is no items
    case send_result['self']['type']
    when 'directory','container' # directory: node, container: shares
      result={ :data => send_result['items'] , :type => :object_list, :textify => lambda { |table_data| c_textify_browse(table_data) } }
      self.format.display_status("Items: #{send_result['item_count']}/#{send_result['total_count']}")
    else # 'file','symbolic_link'
      result={ :data => send_result['self'] , :type => :single_object}
      #result={ :data => [send_result['self']] , :type => :object_list, :textify => lambda { |table_data| c_textify_browse(table_data) } }
      #raise "unknown type: #{send_result['self']['type']}"
    end
    return c_result_remove_prefix_path(result,'path',prefix_path)
  when :upload
    # we send only a list of one transfer request
    transfer_request = { :paths => [ { :destination => self.transfer.destination_folder('send') } ] }
    transfer_request.deep_merge!(@add_request_param)
    send_result=@api_node.create('files/upload_setup',{:transfer_requests => [ { :transfer_request => transfer_request } ] } )[:data]
    # only one request, so only one answer
    transfer_spec=send_result['transfer_specs'].first['transfer_spec']
    # delete this part, as the returned value contains only destination, and not sources
    transfer_spec.delete('paths')
    return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
  when :download
    transfer_request = {:paths => self.transfer.ts_source_paths }
    transfer_request.deep_merge!(@add_request_param)
    send_result=@api_node.create('files/download_setup',{:transfer_requests => [ { :transfer_request => transfer_request } ] } )[:data]
    # only one request, so only one answer
    transfer_spec=send_result['transfer_specs'].first['transfer_spec']
    return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
  when :api_details
    return { :type=>:single_object, :data => @api_node.params }
  end
end
get_next_arg_add_prefix(path_prefix,name,number=:single) click to toggle source

get path arguments from command line, and add prefix

# File lib/asperalm/cli/plugins/node.rb, line 83
def get_next_arg_add_prefix(path_prefix,name,number=:single)
  thepath=self.options.get_next_argument(name,number)
  return thepath if path_prefix.nil?
  return File.join(path_prefix,thepath) if thepath.is_a?(String)
  return thepath.map {|p| File.join(path_prefix,p)} if thepath.is_a?(Array)
  raise StandardError,"expect: nil, String or Array"
end