Fix the crappy error handling in utils.run() - it doesn't work
at all right now, because no exceptions are thrown for a timeout
Move CmdResult class to utils.py - it has no business being under
hosts/ ... nothing to do with it.
Signed-off-by: Martin Bligh <mbligh@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@995 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/server/hosts/__init__.py b/server/hosts/__init__.py
index f849375..f3c250b 100644
--- a/server/hosts/__init__.py
+++ b/server/hosts/__init__.py
@@ -25,6 +25,3 @@
# bootloader classes
from bootloader import Bootloader
-
-# command result class
-from base_classes import CmdResult
diff --git a/server/hosts/base_classes.py b/server/hosts/base_classes.py
index 9625224..4669dee 100644
--- a/server/hosts/base_classes.py
+++ b/server/hosts/base_classes.py
@@ -10,7 +10,6 @@
Host: a machine on which you can run programs
RemoteHost: a remote machine on which you can run programs
- CmdResult: contain the results of a Host.run() command execution
"""
__author__ = """
@@ -19,11 +18,8 @@
stutsman@google.com (Ryan Stutsman)
"""
-
import time
-import textwrap
-import bootloader
-import utils
+import bootloader, utils
class Host(object):
"""
@@ -127,52 +123,3 @@
def __init__(self):
super(RemoteHost, self).__init__()
-
-
-class CmdResult(object):
- """
- Command execution result.
-
- Modified from the original Autoserv code, local_cmd.py:
- Copyright jonmayer@google.com (Jonathan Mayer),
- mbligh@google.com (Martin J. Bligh)
- Released under the GPL, v2
-
- command: String containing the command line itself
- exit_status: Integer exit code of the process
- stdout: String containing stdout of the process
- stderr: String containing stderr of the process
- duration: Elapsed wall clock time running the process
- aborted: Signal that caused the command to terminate (0 if none)
- """
-
- def __init__(self):
- super(CmdResult, self).__init__()
- self.command = ""
- self.exit_status = None
- self.stdout = ""
- self.stderr = ""
- self.duration = 0
- self.aborted= False
-
-
- def __repr__(self):
- wrapper= textwrap.TextWrapper(width=78,
- initial_indent="\n ", subsequent_indent=" ")
-
- stdout= self.stdout.rstrip(" \n")
- if stdout:
- stdout= "\nStdout:\n%s" % (stdout,)
-
- stderr= self.stderr.rstrip(" \n")
- if stderr:
- stderr= "\nStderr:\n%s" % (stderr,)
-
- return ("* Command: %s\n"
- "Exit status: %s\n"
- "Duration: %s\n"
- "Aborted: %s"
- "%s"
- "%s"
- % (wrapper.fill(self.command), self.exit_status,
- self.duration, self.aborted, stdout, stderr))
diff --git a/server/hosts/ssh_host.py b/server/hosts/ssh_host.py
index 2edc6b3..4b18504 100644
--- a/server/hosts/ssh_host.py
+++ b/server/hosts/ssh_host.py
@@ -337,7 +337,7 @@
#~ print "running %s" % (command,)
result= utils.run(r'%s "%s"' % (self.ssh_command(),
utils.sh_escape(command)),
- timeout, ignore_status)
+ timeout, ignore_status)
return result
diff --git a/server/utils.py b/server/utils.py
index ba350ef..7de279f 100644
--- a/server/utils.py
+++ b/server/utils.py
@@ -13,8 +13,9 @@
"""
import atexit, os, select, shutil, signal, StringIO, subprocess, tempfile
-import time, types, urllib, re, sys
+import time, types, urllib, re, sys, textwrap
import hosts, errors
+from error import *
# A dictionary of pid and a list of tmpdirs for that pid
__tmp_dirs = {}
@@ -126,6 +127,24 @@
return tmpfile
+def __nuke_subprocess(subproc):
+ # the process has not terminated within timeout,
+ # kill it via an escalating series of signals.
+ signal_queue = [signal.SIGTERM, signal.SIGKILL]
+ for sig in signal_queue:
+ try:
+ os.kill(subproc.pid, sig)
+ # The process may have died before we could kill it.
+ except OSError:
+ pass
+
+ for i in range(5):
+ rc = subproc.poll()
+ if rc != None:
+ return
+ time.sleep(1)
+
+
def _process_output(pipe, fbuffer, teefile=None, use_os_read=True):
if use_os_read:
data = os.read(pipe.fileno(), 1024)
@@ -161,30 +180,15 @@
pid, exit_status_indication = os.waitpid(subproc.pid,
os.WNOHANG)
if pid:
- break
+ return exit_status_indication
if timeout:
time_left = stop_time - time.time()
# the process has not terminated within timeout,
# kill it via an escalating series of signals.
if not pid:
- signal_queue = [signal.SIGTERM, signal.SIGKILL]
- for sig in signal_queue:
- try:
- os.kill(subproc.pid, sig)
- # handle race condition in which
- # process died before we could kill it.
- except OSError:
- pass
-
- for i in range(5):
- pid, exit_status_indication = (
- os.waitpid(subproc.pid, os.WNOHANG))
- if pid:
- return exit_status_indication
- else:
- time.sleep(1)
- return exit_status_indication
+ __nuke_subprocess(subproc)
+ raise CmdError('Command not complete within %s seconds' % timeout)
def run(command, timeout=None, ignore_status=False,
@@ -206,17 +210,13 @@
stderr_tee: likewise for stderr
Returns:
- a hosts.CmdResult object
+ a CmdResult object
Raises:
AutoservRunError: the exit code of the command
execution was not 0
-
- TODO(poirier): Should a timeout raise an exception? Should
- exceptions be raised at all?
"""
- result = hosts.CmdResult()
- result.command = command
+ result = CmdResult(command)
sp = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True,
shell=True, executable="/bin/bash")
@@ -227,17 +227,10 @@
# We are holding ends to stdin, stdout pipes
# hence we need to be sure to close those fds no mater what
start_time = time.time()
- exit_status_indication = _wait_for_command(
- sp, start_time, timeout,
- stdout_file, stderr_file,
- stdout_tee, stderr_tee)
+ result.exit_status = _wait_for_command(sp, start_time, timeout,
+ stdout_file, stderr_file, stdout_tee, stderr_tee)
result.duration = time.time() - start_time
- result.aborted = exit_status_indication & 127
- if result.aborted:
- result.exit_status = None
- else:
- result.exit_status = exit_status_indication / 256
# don't use os.read now, so we get all the rest of the output
_process_output(sp.stdout, stdout_file, stdout_tee,
use_os_read=False)
@@ -252,8 +245,7 @@
result.stderr = stderr_file.getvalue()
if not ignore_status and result.exit_status > 0:
- raise errors.AutoservRunError("command execution error",
- result)
+ raise errors.AutoservRunError("command execution error", result)
return result
@@ -416,3 +408,44 @@
return ret
else:
return default
+
+
+class CmdResult(object):
+ """
+ Command execution result.
+
+ command: String containing the command line itself
+ exit_status: Integer exit code of the process
+ stdout: String containing stdout of the process
+ stderr: String containing stderr of the process
+ duration: Elapsed wall clock time running the process
+ """
+
+ def __init__(self, command = None):
+ self.command = command
+ self.exit_status = None
+ self.stdout = ""
+ self.stderr = ""
+ self.duration = 0
+
+
+ def __repr__(self):
+ wrapper = textwrap.TextWrapper(width = 78,
+ initial_indent="\n ",
+ subsequent_indent=" ")
+
+ stdout = self.stdout.rstrip()
+ if stdout:
+ stdout = "\nstdout:\n%s" % stdout
+
+ stderr = self.stderr.rstrip()
+ if stderr:
+ stderr = "\nstderr:\n%s" % stderr
+
+ return ("* Command: %s\n"
+ "Exit status: %s\n"
+ "Duration: %s\n"
+ "%s"
+ "%s"
+ % (wrapper.fill(self.command), self.exit_status,
+ self.duration, stdout, stderr))