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
OPS_ARGS.keys.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, arguments) ⇒ 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
30 31 32 |
# File 'lib/aspera/ascmd.rb', line 30 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
152 153 154 155 156 |
# File 'lib/aspera/ascmd.rb', line 152 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
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/aspera/ascmd.rb', line 161 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.trace1{"#{' .' * 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.trace1{"#{' .' * 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.trace1{"#{' .' * 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.trace1{"#{' .' * 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 :sub_struct 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 Aspera.error_unexpected_value(type_descr[:decode]) end return result end |
Instance Method Details
#execute_single(action_sym, arguments) ⇒ Object
execute an “as” command on a remote server
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/aspera/ascmd.rb', line 38 def execute_single(action_sym, arguments) arguments = [] if arguments.nil? Log.log.debug{"execute_single:#{action_sym}:#{arguments}"} Aspera.assert_type(action_sym, Symbol) Aspera.assert_type(arguments, Array) Aspera.assert(arguments.all?(String), 'arguments must be strings') # lines of commands (String's) command_lines = [] # add "as_" command main_command = "as_#{action_sym}" arg_batches = if OPS_ARGS[action_sym].nil? || OPS_ARGS[action_sym].zero? [arguments] else # split arguments into batches arguments.each_slice(OPS_ARGS[action_sym]).to_a end arg_batches.each do |args| command = [main_command] # enclose arguments in double quotes, protect backslash and double quotes args.each do |v| command.push(%Q{"#{v.gsub(/["\\]/){|s|"\\#{s}"}}"}) end command_lines.push(command.join(' ')) end command_lines.push('as_exit') command_lines.push('') # execute the main command and then exit stdin_input = command_lines.join("\n") Log.log.trace1{"execute_single:#{stdin_input}"} # 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 Aspera.error_unexpected_value(result.keys.length) end # raise error as exception raise Error.new(result[:errno], result[:errstr], action_sym, arguments) if result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort) return result end |