| Index: client/tests/kvm/kvm_subprocess.py
 | 
| diff --git a/client/tests/kvm/kvm_subprocess.py b/client/tests/kvm/kvm_subprocess.py
 | 
| index c3e2dd7ecda07aa1892ab7d313412b54d6a5a8ea..8321bb3de778f07e33aac5f3f91e63ef2583a257 100755
 | 
| --- a/client/tests/kvm/kvm_subprocess.py
 | 
| +++ b/client/tests/kvm/kvm_subprocess.py
 | 
| @@ -189,88 +189,6 @@ import subprocess, time, signal, re, threading, logging
 | 
|  import common, kvm_utils
 | 
|  
 | 
|  
 | 
| -class ExpectError(Exception):
 | 
| -    def __init__(self, patterns, output):
 | 
| -        Exception.__init__(self, patterns, output)
 | 
| -        self.patterns = patterns
 | 
| -        self.output = output
 | 
| -
 | 
| -    def _pattern_str(self):
 | 
| -        if len(self.patterns) == 1:
 | 
| -            return "pattern %r" % self.patterns[0]
 | 
| -        else:
 | 
| -            return "patterns %r" % self.patterns
 | 
| -
 | 
| -    def __str__(self):
 | 
| -        return ("Unknown error occurred while looking for %s (output: %r)" %
 | 
| -                (self._pattern_str(), self.output))
 | 
| -
 | 
| -
 | 
| -class ExpectTimeoutError(ExpectError):
 | 
| -    def __str__(self):
 | 
| -        return ("Timeout expired while looking for %s (output: %r)" %
 | 
| -                (self._pattern_str(), self.output))
 | 
| -
 | 
| -
 | 
| -class ExpectProcessTerminatedError(ExpectError):
 | 
| -    def __init__(self, patterns, status, output):
 | 
| -        ExpectError.__init__(self, patterns, output)
 | 
| -        self.status = status
 | 
| -
 | 
| -    def __str__(self):
 | 
| -        return ("Process terminated while looking for %s (status: %s, output: "
 | 
| -                "%r)" % (self._pattern_str(), self.status, self.output))
 | 
| -
 | 
| -
 | 
| -class ShellError(Exception):
 | 
| -    def __init__(self, cmd, output):
 | 
| -        Exception.__init__(self, cmd, output)
 | 
| -        self.cmd = cmd
 | 
| -        self.output = output
 | 
| -
 | 
| -    def __str__(self):
 | 
| -        return ("Could not execute shell command %r (output: %r)" %
 | 
| -                (self.cmd, self.output))
 | 
| -
 | 
| -
 | 
| -class ShellTimeoutError(ShellError):
 | 
| -    def __str__(self):
 | 
| -        return ("Timeout expired while waiting for shell command %r to "
 | 
| -                "complete (output: %r)" % (self.cmd, self.output))
 | 
| -
 | 
| -
 | 
| -class ShellProcessTerminatedError(ShellError):
 | 
| -    # Raised when the shell process itself (e.g. ssh, netcat, telnet)
 | 
| -    # terminates unexpectedly
 | 
| -    def __init__(self, cmd, status, output):
 | 
| -        ShellError.__init__(self, cmd, output)
 | 
| -        self.status = status
 | 
| -
 | 
| -    def __str__(self):
 | 
| -        return ("Shell process terminated while waiting for command %r to "
 | 
| -                "complete (status: %s, output: %r)" %
 | 
| -                (self.cmd, self.status, self.output))
 | 
| -
 | 
| -
 | 
| -class ShellCmdError(ShellError):
 | 
| -    # Raised when a command executed in a shell terminates with a nonzero
 | 
| -    # exit code (status)
 | 
| -    def __init__(self, cmd, status, output):
 | 
| -        ShellError.__init__(self, cmd, output)
 | 
| -        self.status = status
 | 
| -
 | 
| -    def __str__(self):
 | 
| -        return ("Shell command %r failed with status %d (output: %r)" %
 | 
| -                (self.cmd, self.status, self.output))
 | 
| -
 | 
| -
 | 
| -class ShellStatusError(ShellError):
 | 
| -    # Raised when the command's exit status cannot be obtained
 | 
| -    def __str__(self):
 | 
| -        return ("Could not get exit status of command %r (output: %r)" %
 | 
| -                (self.cmd, self.output))
 | 
| -
 | 
| -
 | 
|  def run_bg(command, termination_func=None, output_func=None, output_prefix="",
 | 
|             timeout=1.0):
 | 
|      """
 | 
| @@ -292,12 +210,12 @@ def run_bg(command, termination_func=None, output_func=None, output_prefix="",
 | 
|      @param timeout: Time duration (in seconds) to wait for the subprocess to
 | 
|              terminate before returning
 | 
|  
 | 
| -    @return: A Tail object.
 | 
| +    @return: A kvm_tail object.
 | 
|      """
 | 
| -    process = Tail(command=command,
 | 
| -                   termination_func=termination_func,
 | 
| -                   output_func=output_func,
 | 
| -                   output_prefix=output_prefix)
 | 
| +    process = kvm_tail(command=command,
 | 
| +                       termination_func=termination_func,
 | 
| +                       output_func=output_func,
 | 
| +                       output_prefix=output_prefix)
 | 
|  
 | 
|      end_time = time.time() + timeout
 | 
|      while time.time() < end_time and process.is_alive():
 | 
| @@ -338,7 +256,7 @@ def run_fg(command, output_func=None, output_prefix="", timeout=1.0):
 | 
|      return (status, output)
 | 
|  
 | 
|  
 | 
| -class Spawn:
 | 
| +class kvm_spawn:
 | 
|      """
 | 
|      This class is used for spawning and controlling a child process.
 | 
|  
 | 
| @@ -350,7 +268,7 @@ class Spawn:
 | 
|      The text file can be accessed at any time using get_output().
 | 
|      In addition, the server opens as many pipes as requested by the client and
 | 
|      writes the output to them.
 | 
| -    The pipes are requested and accessed by classes derived from Spawn.
 | 
| +    The pipes are requested and accessed by classes derived from kvm_spawn.
 | 
|      These pipes are referred to as "readers".
 | 
|      The server also receives input from the client and sends it to the child
 | 
|      process.
 | 
| @@ -634,7 +552,7 @@ _thread_kill_requested = False
 | 
|  
 | 
|  def kill_tail_threads():
 | 
|      """
 | 
| -    Kill all Tail threads.
 | 
| +    Kill all kvm_tail threads.
 | 
|  
 | 
|      After calling this function no new threads should be started.
 | 
|      """
 | 
| @@ -646,12 +564,12 @@ def kill_tail_threads():
 | 
|      _thread_kill_requested = False
 | 
|  
 | 
|  
 | 
| -class Tail(Spawn):
 | 
| +class kvm_tail(kvm_spawn):
 | 
|      """
 | 
|      This class runs a child process in the background and sends its output in
 | 
|      real time, line-by-line, to a callback function.
 | 
|  
 | 
| -    See Spawn's docstring.
 | 
| +    See kvm_spawn's docstring.
 | 
|  
 | 
|      This class uses a single pipe reader to read data in real time from the
 | 
|      child process and report it to a given callback function.
 | 
| @@ -692,10 +610,10 @@ class Tail(Spawn):
 | 
|          """
 | 
|          # Add a reader and a close hook
 | 
|          self._add_reader("tail")
 | 
| -        self._add_close_hook(Tail._join_thread)
 | 
| +        self._add_close_hook(kvm_tail._join_thread)
 | 
|  
 | 
|          # Init the superclass
 | 
| -        Spawn.__init__(self, command, id, auto_close, echo, linesep)
 | 
| +        kvm_spawn.__init__(self, command, id, auto_close, echo, linesep)
 | 
|  
 | 
|          # Remember some attributes
 | 
|          self.termination_func = termination_func
 | 
| @@ -711,11 +629,11 @@ class Tail(Spawn):
 | 
|  
 | 
|  
 | 
|      def __getinitargs__(self):
 | 
| -        return Spawn.__getinitargs__(self) + (self.termination_func,
 | 
| -                                              self.termination_params,
 | 
| -                                              self.output_func,
 | 
| -                                              self.output_params,
 | 
| -                                              self.output_prefix)
 | 
| +        return kvm_spawn.__getinitargs__(self) + (self.termination_func,
 | 
| +                                                  self.termination_params,
 | 
| +                                                  self.output_func,
 | 
| +                                                  self.output_params,
 | 
| +                                                  self.output_prefix)
 | 
|  
 | 
|  
 | 
|      def set_termination_func(self, termination_func):
 | 
| @@ -847,15 +765,15 @@ class Tail(Spawn):
 | 
|              t.join()
 | 
|  
 | 
|  
 | 
| -class Expect(Tail):
 | 
| +class kvm_expect(kvm_tail):
 | 
|      """
 | 
|      This class runs a child process in the background and provides expect-like
 | 
|      services.
 | 
|  
 | 
| -    It also provides all of Tail's functionality.
 | 
| +    It also provides all of kvm_tail's functionality.
 | 
|      """
 | 
|  
 | 
| -    def __init__(self, command=None, id=None, auto_close=True, echo=False,
 | 
| +    def __init__(self, command=None, id=None, auto_close=False, echo=False,
 | 
|                   linesep="\n", termination_func=None, termination_params=(),
 | 
|                   output_func=None, output_params=(), output_prefix=""):
 | 
|          """
 | 
| @@ -888,13 +806,13 @@ class Expect(Tail):
 | 
|          self._add_reader("expect")
 | 
|  
 | 
|          # Init the superclass
 | 
| -        Tail.__init__(self, command, id, auto_close, echo, linesep,
 | 
| -                      termination_func, termination_params,
 | 
| -                      output_func, output_params, output_prefix)
 | 
| +        kvm_tail.__init__(self, command, id, auto_close, echo, linesep,
 | 
| +                          termination_func, termination_params,
 | 
| +                          output_func, output_params, output_prefix)
 | 
|  
 | 
|  
 | 
|      def __getinitargs__(self):
 | 
| -        return Tail.__getinitargs__(self)
 | 
| +        return kvm_tail.__getinitargs__(self)
 | 
|  
 | 
|  
 | 
|      def read_nonblocking(self, timeout=None):
 | 
| @@ -940,7 +858,7 @@ class Expect(Tail):
 | 
|  
 | 
|  
 | 
|      def read_until_output_matches(self, patterns, filter=lambda x: x,
 | 
| -                                  timeout=60, internal_timeout=None,
 | 
| +                                  timeout=30.0, internal_timeout=None,
 | 
|                                    print_func=None):
 | 
|          """
 | 
|          Read using read_nonblocking until a match is found using match_patterns,
 | 
| @@ -958,14 +876,13 @@ class Expect(Tail):
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
| -        @return: Tuple containing the match index and the data read so far
 | 
| -        @raise ExpectTimeoutError: Raised if timeout expires
 | 
| -        @raise ExpectProcessTerminatedError: Raised if the child process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ExpectError: Raised if an unknown error occurs
 | 
| +        @return: Tuple containing the match index (or None if no match was
 | 
| +                found) and the data read so far.
 | 
|          """
 | 
| +        match = None
 | 
| +        data = ""
 | 
| +
 | 
|          fd = self._get_fd("expect")
 | 
| -        o = ""
 | 
|          end_time = time.time() + timeout
 | 
|          while True:
 | 
|              try:
 | 
| @@ -973,31 +890,41 @@ class Expect(Tail):
 | 
|                                          max(0, end_time - time.time()))
 | 
|              except (select.error, TypeError):
 | 
|                  break
 | 
| -            if not r:
 | 
| -                raise ExpectTimeoutError(patterns, o)
 | 
| -            # Read data from child
 | 
| -            data = self.read_nonblocking(internal_timeout)
 | 
| -            if not data:
 | 
| +            if fd not in r:
 | 
|                  break
 | 
| +            # Read data from child
 | 
| +            newdata = self.read_nonblocking(internal_timeout)
 | 
|              # Print it if necessary
 | 
| -            if print_func:
 | 
| -                for line in data.splitlines():
 | 
| +            if print_func and newdata:
 | 
| +                str = newdata
 | 
| +                if str.endswith("\n"):
 | 
| +                    str = str[:-1]
 | 
| +                for line in str.split("\n"):
 | 
|                      print_func(line)
 | 
| +            data += newdata
 | 
| +
 | 
| +            done = False
 | 
|              # Look for patterns
 | 
| -            o += data
 | 
| -            match = self.match_patterns(filter(o), patterns)
 | 
| +            match = self.match_patterns(filter(data), patterns)
 | 
|              if match is not None:
 | 
| -                return match, o
 | 
| +                done = True
 | 
| +            # Check if child has died
 | 
| +            if not self.is_alive():
 | 
| +                logging.debug("Process terminated with status %s" %
 | 
| +                              self.get_status())
 | 
| +                done = True
 | 
| +            # Are we done?
 | 
| +            if done: break
 | 
|  
 | 
| -        # Check if the child has terminated
 | 
| -        if kvm_utils.wait_for(lambda: not self.is_alive(), 5, 0, 0.1):
 | 
| -            raise ExpectProcessTerminatedError(patterns, self.get_status(), o)
 | 
| -        else:
 | 
| -            # This shouldn't happen
 | 
| -            raise ExpectError(patterns, o)
 | 
| +        # Print some debugging info
 | 
| +        if match is None and (self.is_alive() or self.get_status() != 0):
 | 
| +            logging.debug("Timeout elapsed or process terminated. Output:" +
 | 
| +                          kvm_utils.format_str_for_message(data.strip()))
 | 
|  
 | 
| +        return (match, data)
 | 
|  
 | 
| -    def read_until_last_word_matches(self, patterns, timeout=60,
 | 
| +
 | 
| +    def read_until_last_word_matches(self, patterns, timeout=30.0,
 | 
|                                       internal_timeout=None, print_func=None):
 | 
|          """
 | 
|          Read using read_nonblocking until the last word of the output matches
 | 
| @@ -1009,11 +936,8 @@ class Expect(Tail):
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
| -        @return: A tuple containing the match index and the data read so far
 | 
| -        @raise ExpectTimeoutError: Raised if timeout expires
 | 
| -        @raise ExpectProcessTerminatedError: Raised if the child process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ExpectError: Raised if an unknown error occurs
 | 
| +        @return: A tuple containing the match index (or None if no match was
 | 
| +                found) and the data read so far.
 | 
|          """
 | 
|          def get_last_word(str):
 | 
|              if str:
 | 
| @@ -1026,7 +950,7 @@ class Expect(Tail):
 | 
|                                                print_func)
 | 
|  
 | 
|  
 | 
| -    def read_until_last_line_matches(self, patterns, timeout=60,
 | 
| +    def read_until_last_line_matches(self, patterns, timeout=30.0,
 | 
|                                       internal_timeout=None, print_func=None):
 | 
|          """
 | 
|          Read using read_nonblocking until the last non-empty line of the output
 | 
| @@ -1043,11 +967,6 @@ class Expect(Tail):
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
| -        @return: A tuple containing the match index and the data read so far
 | 
| -        @raise ExpectTimeoutError: Raised if timeout expires
 | 
| -        @raise ExpectProcessTerminatedError: Raised if the child process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ExpectError: Raised if an unknown error occurs
 | 
|          """
 | 
|          def get_last_nonempty_line(str):
 | 
|              nonempty_lines = [l for l in str.splitlines() if l.strip()]
 | 
| @@ -1061,12 +980,12 @@ class Expect(Tail):
 | 
|                                                print_func)
 | 
|  
 | 
|  
 | 
| -class ShellSession(Expect):
 | 
| +class kvm_shell_session(kvm_expect):
 | 
|      """
 | 
|      This class runs a child process in the background.  It it suited for
 | 
|      processes that provide an interactive shell, such as SSH and Telnet.
 | 
|  
 | 
| -    It provides all services of Expect and Tail.  In addition, it
 | 
| +    It provides all services of kvm_expect and kvm_tail.  In addition, it
 | 
|      provides command running services, and a utility function to test the
 | 
|      process for responsiveness.
 | 
|      """
 | 
| @@ -1103,12 +1022,12 @@ class ShellSession(Expect):
 | 
|          @param prompt: Regular expression describing the shell's prompt line.
 | 
|          @param status_test_command: Command to be used for getting the last
 | 
|                  exit status of commands run inside the shell (used by
 | 
| -                cmd_status_output() and friends).
 | 
| +                get_command_status_output() and friends).
 | 
|          """
 | 
|          # Init the superclass
 | 
| -        Expect.__init__(self, command, id, auto_close, echo, linesep,
 | 
| -                        termination_func, termination_params,
 | 
| -                        output_func, output_params, output_prefix)
 | 
| +        kvm_expect.__init__(self, command, id, auto_close, echo, linesep,
 | 
| +                            termination_func, termination_params,
 | 
| +                            output_func, output_params, output_prefix)
 | 
|  
 | 
|          # Remember some attributes
 | 
|          self.prompt = prompt
 | 
| @@ -1116,8 +1035,8 @@ class ShellSession(Expect):
 | 
|  
 | 
|  
 | 
|      def __getinitargs__(self):
 | 
| -        return Expect.__getinitargs__(self) + (self.prompt,
 | 
| -                                               self.status_test_command)
 | 
| +        return kvm_expect.__getinitargs__(self) + (self.prompt,
 | 
| +                                                   self.status_test_command)
 | 
|  
 | 
|  
 | 
|      def set_prompt(self, prompt):
 | 
| @@ -1166,7 +1085,7 @@ class ShellSession(Expect):
 | 
|          return False
 | 
|  
 | 
|  
 | 
| -    def read_up_to_prompt(self, timeout=60, internal_timeout=None,
 | 
| +    def read_up_to_prompt(self, timeout=30.0, internal_timeout=None,
 | 
|                            print_func=None):
 | 
|          """
 | 
|          Read using read_nonblocking until the last non-empty line of the output
 | 
| @@ -1182,34 +1101,31 @@ class ShellSession(Expect):
 | 
|          @param print_func: A function to be used to print the data being
 | 
|                  read (should take a string parameter)
 | 
|  
 | 
| -        @return: The data read so far
 | 
| -        @raise ExpectTimeoutError: Raised if timeout expires
 | 
| -        @raise ExpectProcessTerminatedError: Raised if the shell process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ExpectError: Raised if an unknown error occurs
 | 
| +        @return: A tuple containing True/False indicating whether the prompt
 | 
| +                was found, and the data read so far.
 | 
|          """
 | 
| -        m, o = self.read_until_last_line_matches([self.prompt], timeout,
 | 
| -                                                 internal_timeout, print_func)
 | 
| -        return o
 | 
| +        (match, output) = self.read_until_last_line_matches([self.prompt],
 | 
| +                                                            timeout,
 | 
| +                                                            internal_timeout,
 | 
| +                                                            print_func)
 | 
| +        return (match is not None, output)
 | 
|  
 | 
|  
 | 
| -    def cmd_output(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                   print_func=None):
 | 
| +    def get_command_status_output(self, command, timeout=30.0,
 | 
| +                                  internal_timeout=None, print_func=None):
 | 
|          """
 | 
| -        Send a command and return its output.
 | 
| +        Send a command and return its exit status and output.
 | 
|  
 | 
| -        @param cmd: Command to send (must not contain newline characters)
 | 
| -        @param timeout: The duration (in seconds) to wait for the prompt to
 | 
| -                return
 | 
| +        @param command: Command to send (must not contain newline characters)
 | 
| +        @param timeout: The duration (in seconds) to wait until a match is
 | 
| +                found
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
|  
 | 
| -        @return: The output of cmd
 | 
| -        @raise ShellTimeoutError: Raised if timeout expires
 | 
| -        @raise ShellProcessTerminatedError: Raised if the shell process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ShellError: Raised if an unknown error occurs
 | 
| +        @return: A tuple (status, output) where status is the exit status or
 | 
| +                None if no exit status is available (e.g. timeout elapsed), and
 | 
| +                output is the output of command.
 | 
|          """
 | 
|          def remove_command_echo(str, cmd):
 | 
|              if str and str.splitlines()[0] == cmd:
 | 
| @@ -1219,132 +1135,79 @@ class ShellSession(Expect):
 | 
|          def remove_last_nonempty_line(str):
 | 
|              return "".join(str.rstrip().splitlines(True)[:-1])
 | 
|  
 | 
| -        logging.debug("Sending command: %s" % cmd)
 | 
| -        self.read_nonblocking(timeout=0)
 | 
| -        self.sendline(cmd)
 | 
| -        try:
 | 
| -            o = self.read_up_to_prompt(timeout, internal_timeout, print_func)
 | 
| -        except ExpectError, e:
 | 
| -            o = remove_command_echo(e.output, cmd)
 | 
| -            if isinstance(e, ExpectTimeoutError):
 | 
| -                raise ShellTimeoutError(cmd, o)
 | 
| -            elif isinstance(e, ExpectProcessTerminatedError):
 | 
| -                raise ShellProcessTerminatedError(cmd, e.status, o)
 | 
| -            else:
 | 
| -                raise ShellError(cmd, o)
 | 
| -
 | 
| -        # Remove the echoed command and the final shell prompt
 | 
| -        return remove_last_nonempty_line(remove_command_echo(o, cmd))
 | 
| +        # Print some debugging info
 | 
| +        logging.debug("Sending command: %s" % command)
 | 
|  
 | 
| +        # Read everything that's waiting to be read
 | 
| +        self.read_nonblocking(timeout=0)
 | 
|  
 | 
| -    def cmd_status_output(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                          print_func=None):
 | 
| -        """
 | 
| -        Send a command and return its exit status and output.
 | 
| -
 | 
| -        @param cmd: Command to send (must not contain newline characters)
 | 
| -        @param timeout: The duration (in seconds) to wait for the prompt to
 | 
| -                return
 | 
| -        @param internal_timeout: The timeout to pass to read_nonblocking
 | 
| -        @param print_func: A function to be used to print the data being read
 | 
| -                (should take a string parameter)
 | 
| +        # Send the command and get its output
 | 
| +        self.sendline(command)
 | 
| +        (match, output) = self.read_up_to_prompt(timeout, internal_timeout,
 | 
| +                                                 print_func)
 | 
| +        # Remove the echoed command from the output
 | 
| +        output = remove_command_echo(output, command)
 | 
| +        # If the prompt was not found, return the output so far
 | 
| +        if not match:
 | 
| +            return (None, output)
 | 
| +        # Remove the final shell prompt from the output
 | 
| +        output = remove_last_nonempty_line(output)
 | 
| +
 | 
| +        # Send the 'echo ...' command to get the last exit status
 | 
| +        self.sendline(self.status_test_command)
 | 
| +        (match, status) = self.read_up_to_prompt(10.0, internal_timeout)
 | 
| +        if not match:
 | 
| +            return (None, output)
 | 
| +        status = remove_command_echo(status, self.status_test_command)
 | 
| +        status = remove_last_nonempty_line(status)
 | 
| +        # Get the first line consisting of digits only
 | 
| +        digit_lines = [l for l in status.splitlines() if l.strip().isdigit()]
 | 
| +        if not digit_lines:
 | 
| +            return (None, output)
 | 
| +        status = int(digit_lines[0].strip())
 | 
|  
 | 
| -        @return: A tuple (status, output) where status is the exit status and
 | 
| -                output is the output of cmd
 | 
| -        @raise ShellTimeoutError: Raised if timeout expires
 | 
| -        @raise ShellProcessTerminatedError: Raised if the shell process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ShellStatusError: Raised if the exit status cannot be obtained
 | 
| -        @raise ShellError: Raised if an unknown error occurs
 | 
| -        """
 | 
| -        o = self.cmd_output(cmd, timeout, internal_timeout, print_func)
 | 
| -        try:
 | 
| -            # Send the 'echo $?' (or equivalent) command to get the exit status
 | 
| -            s = self.cmd_output(self.status_test_command, 10, internal_timeout)
 | 
| -        except ShellError:
 | 
| -            raise ShellStatusError(cmd, o)
 | 
| +        # Print some debugging info
 | 
| +        if status != 0:
 | 
| +            logging.debug("Command failed; status: %d, output:%s", status,
 | 
| +                          kvm_utils.format_str_for_message(output.strip()))
 | 
|  
 | 
| -        # Get the first line consisting of digits only
 | 
| -        digit_lines = [l for l in s.splitlines() if l.strip().isdigit()]
 | 
| -        if digit_lines:
 | 
| -            return int(digit_lines[0].strip()), o
 | 
| -        else:
 | 
| -            raise ShellStatusError(cmd, o)
 | 
| +        return (status, output)
 | 
|  
 | 
|  
 | 
| -    def cmd_status(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                   print_func=None):
 | 
| +    def get_command_status(self, command, timeout=30.0, internal_timeout=None,
 | 
| +                           print_func=None):
 | 
|          """
 | 
|          Send a command and return its exit status.
 | 
|  
 | 
| -        @param cmd: Command to send (must not contain newline characters)
 | 
| -        @param timeout: The duration (in seconds) to wait for the prompt to
 | 
| -                return
 | 
| +        @param command: Command to send
 | 
| +        @param timeout: The duration (in seconds) to wait until a match is
 | 
| +                found
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
|  
 | 
| -        @return: The exit status of cmd
 | 
| -        @raise ShellTimeoutError: Raised if timeout expires
 | 
| -        @raise ShellProcessTerminatedError: Raised if the shell process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ShellStatusError: Raised if the exit status cannot be obtained
 | 
| -        @raise ShellError: Raised if an unknown error occurs
 | 
| +        @return: Exit status or None if no exit status is available (e.g.
 | 
| +                timeout elapsed).
 | 
|          """
 | 
| -        s, o = self.cmd_status_output(cmd, timeout, internal_timeout,
 | 
| -                                      print_func)
 | 
| -        return s
 | 
| +        (status, output) = self.get_command_status_output(command, timeout,
 | 
| +                                                          internal_timeout,
 | 
| +                                                          print_func)
 | 
| +        return status
 | 
|  
 | 
|  
 | 
| -    def cmd(self, cmd, timeout=60, internal_timeout=None, print_func=None):
 | 
| +    def get_command_output(self, command, timeout=30.0, internal_timeout=None,
 | 
| +                           print_func=None):
 | 
|          """
 | 
| -        Send a command and return its output. If the command's exit status is
 | 
| -        nonzero, raise an exception.
 | 
| +        Send a command and return its output.
 | 
|  
 | 
| -        @param cmd: Command to send (must not contain newline characters)
 | 
| -        @param timeout: The duration (in seconds) to wait for the prompt to
 | 
| -                return
 | 
| +        @param command: Command to send
 | 
| +        @param timeout: The duration (in seconds) to wait until a match is
 | 
| +                found
 | 
|          @param internal_timeout: The timeout to pass to read_nonblocking
 | 
|          @param print_func: A function to be used to print the data being read
 | 
|                  (should take a string parameter)
 | 
| -
 | 
| -        @return: The output of cmd
 | 
| -        @raise ShellTimeoutError: Raised if timeout expires
 | 
| -        @raise ShellProcessTerminatedError: Raised if the shell process
 | 
| -                terminates while waiting for output
 | 
| -        @raise ShellError: Raised if the exit status cannot be obtained or if
 | 
| -                an unknown error occurs
 | 
| -        @raise ShellStatusError: Raised if the exit status cannot be obtained
 | 
| -        @raise ShellError: Raised if an unknown error occurs
 | 
| -        @raise ShellCmdError: Raised if the exit status is nonzero
 | 
| -        """
 | 
| -        s, o = self.cmd_status_output(cmd, timeout, internal_timeout,
 | 
| -                                      print_func)
 | 
| -        if s != 0:
 | 
| -            raise ShellCmdError(cmd, s, o)
 | 
| -        return o
 | 
| -
 | 
| -
 | 
| -    def get_command_output(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                           print_func=None):
 | 
| -        """
 | 
| -        Alias for cmd_output() for backward compatibility.
 | 
| -        """
 | 
| -        return self.cmd_output(cmd, timeout, internal_timeout, print_func)
 | 
| -
 | 
| -
 | 
| -    def get_command_status_output(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                                  print_func=None):
 | 
| -        """
 | 
| -        Alias for cmd_status_output() for backward compatibility.
 | 
| -        """
 | 
| -        return self.cmd_status_output(cmd, timeout, internal_timeout,
 | 
| -                                      print_func)
 | 
| -
 | 
| -
 | 
| -    def get_command_status(self, cmd, timeout=60, internal_timeout=None,
 | 
| -                           print_func=None):
 | 
| -        """
 | 
| -        Alias for cmd_status() for backward compatibility.
 | 
|          """
 | 
| -        return self.cmd_status(cmd, timeout, internal_timeout, print_func)
 | 
| +        (status, output) = self.get_command_status_output(command, timeout,
 | 
| +                                                          internal_timeout,
 | 
| +                                                          print_func)
 | 
| +        return output
 | 
| 
 |