Class: Aspera::AsCmd
- Inherits:
-
Object
- Object
- Aspera::AsCmd
- Defined in:
- lib/aspera/ascmd.rb
Overview
Run ascmd commands using specified executor (usually, remotely on transfer node) Equivalent of SDK “command client” execute: “ascmd -h” to get syntax Note: “ls” can take filters: as_ls -f *.txt -f *.bin /
Defined Under Namespace
Classes: Error
Constant Summary collapse
- OPERATIONS =
list of supported actions
%i[ls rm mv du info mkdir cp df md5sum].freeze
Class Method Summary collapse
-
.field_description(struct_name, typed_buffer) ⇒ Object
get description of structure’s field, @param struct_name, @param typed_buffer provides field name.
-
.parse(buffer, type_name, indent_level = nil) ⇒ Object
decodes the provided buffer as provided type name :base : value, :buffer_list : an array of btype,buffer, :field_list : a hash, or array.
Instance Method Summary collapse
-
#execute_single(action_sym, args = nil) ⇒ Object
execute an “as” command on a remote server.
-
#initialize(command_executor) ⇒ AsCmd
constructor
@param command_executor [Object] provides the “execute” method, taking a command to execute, and stdin to feed to it, typically: ssh or local.
Constructor Details
#initialize(command_executor) ⇒ AsCmd
@param command_executor [Object] provides the “execute” method, taking a command to execute, and stdin to feed to it, typically: ssh or local
15 16 17 |
# File 'lib/aspera/ascmd.rb', line 15 def initialize(command_executor) @command_executor = command_executor end |
Class Method Details
.field_description(struct_name, typed_buffer) ⇒ Object
get description of structure’s field, @param struct_name, @param typed_buffer provides field name
108 109 110 111 112 |
# File 'lib/aspera/ascmd.rb', line 108 def field_description(struct_name, typed_buffer) result = TYPES_DESCR[struct_name][:fields][typed_buffer[:btype] - ENUM_START] raise "Unrecognized field for #{struct_name}: #{typed_buffer[:btype]}\n#{typed_buffer[:buffer]}" if result.nil? return result end |
.parse(buffer, type_name, indent_level = nil) ⇒ Object
decodes the provided buffer as provided type name :base : value, :buffer_list : an array of btype,buffer, :field_list : a hash, or array
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/aspera/ascmd.rb', line 117 def parse(buffer, type_name, indent_level=nil) indent_level = (indent_level || -1) + 1 type_descr = TYPES_DESCR[type_name] raise "Unexpected type #{type_name}" if type_descr.nil? Log.log.debug{"#{' .' * indent_level}parse:#{type_name}:#{type_descr[:decode]}:#{buffer[0, 16]}...".red} result = nil case type_descr[:decode] when :base num_bytes = type_name.eql?(:zstr) ? buffer.length : type_descr[:size] raise 'ERROR:not enough bytes' if buffer.length < num_bytes byte_array = buffer.shift(num_bytes) byte_array = [byte_array] unless byte_array.is_a?(Array) result = byte_array.pack('C*').unpack1(type_descr[:unpack]) result.force_encoding('UTF-8') if type_name.eql?(:zstr) Log.log.debug{"#{' .' * indent_level}-> base:#{byte_array} -> #{result}"} result = Time.at(result) if type_name.eql?(:epoch) when :buffer_list result = [] until buffer.empty? btype = parse(buffer, :int8, indent_level) length = parse(buffer, :int32, indent_level) raise 'ERROR:not enough bytes' if buffer.length < length value = buffer.shift(length) result.push({btype: btype, buffer: value}) Log.log.debug{"#{' .' * indent_level}:buffer_list[#{result.length - 1}] #{result.last}"} end when :field_list # by default the result is one struct result = {} # get individual binary fields parse(buffer, :blist, indent_level).each do |typed_buffer| # what type of field is it ? field_info = field_description(type_name, typed_buffer) Log.log.debug{"#{' .' * indent_level}+ field(special=#{field_info[:special]})=#{field_info[:name]}".green} case field_info[:special] when nil result[field_info[:name]] = parse(typed_buffer[:buffer], field_info[:is_a], indent_level) when :return_true result[field_info[:name]] = true when :substruct result[field_info[:name]] = parse(typed_buffer[:buffer], :blist, indent_level).map{|r|parse(r[:buffer], field_info[:is_a], indent_level)} when :multiple result[field_info[:name]] ||= [] result[field_info[:name]].push(parse(typed_buffer[:buffer], field_info[:is_a], indent_level)) when :restart_on_first fl = result[field_info[:name]] = [] parse(typed_buffer[:buffer], :blist, indent_level).map do |tb| fl.push({}) if tb[:btype].eql?(ENUM_START) fi = field_description(field_info[:is_a], tb) fl.last[fi[:name]] = parse(tb[:buffer], fi[:is_a], indent_level) end end end else raise "error: unknown decode:#{type_descr[:decode]}" end # is_a return result end |
Instance Method Details
#execute_single(action_sym, args = nil) ⇒ Object
execute an “as” command on a remote server
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/aspera/ascmd.rb', line 23 def execute_single(action_sym, args=nil) # concatenate arguments, enclose in double quotes, protect backslash and double quotes, add "as_" command and as_exit stdin_input = (args || []).map{|v| '"' + v.gsub(/["\\]/n) {|s| '\\' + s } + '"'}.unshift('as_' + action_sym.to_s).join(' ') + "\nas_exit\n" # execute, get binary output byte_buffer = @command_executor.execute('ascmd', stdin_input).unpack('C*') raise 'ERROR: empty answer from server' if byte_buffer.empty? # get hash or table result result = self.class.parse(byte_buffer, :result) raise 'ERROR: unparsed bytes remaining' unless byte_buffer.empty? # get and delete info,always present in results system_info = result[:info] result.delete(:info) # make single file result like a folder result[:dir] = [result.delete(:file)] if result.key?(:file) # add type field for stats if result.key?(:dir) result[:dir].each do |file| if file.key?(:smode) # Converts the first character of the file mode (see 'man ls') into a type. file[:type] = case file[:smode][0, 1]; when 'd' then:directory; when '-' then:file; when 'l' then:link; else; :other; end # rubocop:disable Style/Semicolon end end end # for info, second overrides first, so restore it case result.keys.length; when 0 then result = system_info; when 1 then result = result[result.keys.first]; else raise 'error'; end # raise error as exception raise Error.new(result[:errno], result[:errstr], action_sym, args) if result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort) return result end |