Update changelog for 0.20.
[tails-test.git] / features / support / helpers / exec_helper.rb
blob94ef578d4a8ce476fad3b13218e3f1428d223919
1 require 'json'
2 require 'socket'
4 class VMCommand
6   attr_reader :returncode, :stdout, :stderr
8   def initialize(vm, cmd, options = {})
9     @returncode, @stdout, @stderr = VMCommand.execute(vm, cmd, options)
10   end
12   def VMCommand.wait_until_remote_shell_is_up(vm, timeout = 30)
13     begin
14       SystemTimer.timeout(timeout) do
15         VMCommand.execute(vm, "true", { :user => "root", :spawn => false })
16       end
17     rescue Timeout::Error
18       raise "Remote shell seems to be down"
19     end
20   end
22   # The parameter `cmd` cannot contain newlines. Separate multiple
23   # commands using ";" instead.
24   # If `:spawn` is false the server will block until it has finished
25   # executing `cmd`. If it's true the server won't block, and the
26   # response will always be [0, "", ""] (only used as an
27   # ACK). execute() will always block until a response is received,
28   # though. Spawning is useful when starting processes in the
29   # background (or running scripts that does the same) like the
30   # vidalia-wrapper, or any application we want to interact with.
31   def VMCommand.execute(vm, cmd, options = {})
32     options[:user] ||= "root"
33     options[:spawn] ||= false
34     type = options[:spawn] ? "spawn" : "call"
35     socket = TCPSocket.new("127.0.0.1", vm.get_remote_shell_port)
36     STDERR.puts "#{type}ing as #{options[:user]}: #{cmd}" if $debug
37     begin
38       socket.puts(JSON.dump([type, options[:user], cmd]))
39       s = socket.readline(sep = "\0").chomp("\0")
40     ensure
41       socket.close
42     end
43     STDERR.puts "#{type} returned: #{s}" if $debug
44     begin
45       return JSON.load(s)
46     rescue JSON::ParserError
47       # The server often returns something unparsable for the very
48       # first execute() command issued after a VM start/restore
49       # (generally from wait_until_remote_shell_is_up()) presumably
50       # because the TCP -> serial link isn't properly setup yet. All
51       # will be well after that initial hickup, so we just retry.
52       return VMCommand.execute(vm, cmd, options)
53     end
54   end
56   def success?
57     return @returncode == 0
58   end
60 end