Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Unified Diff: client/tests/kvm/kvm_subprocess.py

Issue 6124004: Revert "Merge remote branch 'cros/upstream' into autotest-rebase" (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git@master
Patch Set: Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « client/tests/kvm/kvm_scheduler.py ('k') | client/tests/kvm/kvm_test_utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « client/tests/kvm/kvm_scheduler.py ('k') | client/tests/kvm/kvm_test_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698