Package: piuparts
Version: 0.41
Severity: wishlist
Tags: patch

Attached patch addresses TODO item in subject

Note the max time is controlled by a hard-coded value in
piuparts-slave (MAX_WAIT_TEST_RUN), which I set to 45
minutes. Please adjust based on your better judgement.

This may/will(?) require some additional mods in the reporting
scripts to detect. When a process is killed after the max wait
period, the log has the following appended:

*** Process KILLED - exceed maximum run time ***
diff --git a/piuparts-slave.py b/piuparts-slave.py
--- a/piuparts-slave.py
+++ b/piuparts-slave.py
@@ -28,6 +28,7 @@
 import stat
 import time
 import logging
+from signal import alarm, signal, SIGALRM, SIGKILL
 import subprocess
 import ConfigParser
 
@@ -36,7 +37,7 @@
 
 
 CONFIG_FILE = "/etc/piuparts/piuparts.conf"
-
+MAX_WAIT_TEST_RUN = 45*60
 
 def setup_logging(log_level, log_file_name):
     logger = logging.getLogger()
@@ -83,6 +84,12 @@
             }, "")
 
 
+class Alarm(Exception):
+    pass
+
+def alarm_handler(signum, frame):
+    raise Alarm
+
 class MasterNotOK(Exception):
 
     def __init__(self):
@@ -337,6 +344,41 @@
     else:
         return False
 
+def get_process_children(pid):
+    p = subprocess.Popen('ps --no-headers -o pid --ppid %d' % pid,
+           shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+    stdout, stderr = p.communicate()
+    return [int(p) for p in stdout.split()]
+
+def run_test_with_timeout(cmd, maxwait, kill_all):
+      logging.debug("Executing: %s" % cmd)
+
+      stdout = ""
+      sh_cmd = "{ %s; } 2>&1" % cmd
+      p = subprocess.Popen(sh_cmd, shell=True,
+                     stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+      if maxwait > 0:
+          signal(SIGALRM, alarm_handler)
+          alarm(maxwait)
+      try:
+          stdout, stderr = p.communicate()
+          if maxwait > 0:
+              alarm(0)
+      except Alarm:
+          pids = [p.pid]
+          if kill_all:
+              pids.extend(get_process_children(p.pid))
+          for pid in pids:
+              if pid > 0:
+                  try: 
+                      os.kill(pid, SIGKILL)
+                  except OSError:
+                      pass
+          return -1,stdout
+
+      return p.returncode,stdout 
+
+
 def test_package(config, package, packages_files):
     logging.info("Testing package %s/%s %s" % (config.section, package["Package"], package["Version"]))
 
@@ -351,54 +393,46 @@
     output.write("\n")
    
     # omit distro test if chroot-tgz is not specified.
+    ret = 0
     if config["chroot-tgz"]: 
-      command = "%(piuparts-cmd)s -ad %(distro)s -b %(chroot-tgz)s" % \
-                  config
-      if config["keep-sources-list"] in ["yes", "true"]:
-          command += " --keep-sources-list "
-    
-      if config["mirror"]:
-          command += " --mirror %s " % config["mirror"]
-      command += " " + package["Package"]
+        command = "%(piuparts-cmd)s -ad %(distro)s -b %(chroot-tgz)s" % \
+                    config
+        if config["keep-sources-list"] in ["yes", "true"]:
+            command += " --keep-sources-list "
+        if config["mirror"]:
+            command += " --mirror %s " % config["mirror"]
+        command += " " + package["Package"]
+  
+        output.write("Executing: %s\n" % command)
+        ret,f = run_test_with_timeout(command, MAX_WAIT_TEST_RUN, True)
+        if ret < 0:
+            output.write(f + "\n *** Process KILLED - exceed maximum run time ***\n")
+        else:
+            output.write(f)
 
-      logging.debug("Executing: %s" % command)
-      output.write("Executing: %s\n" % command)
-      f = os.popen("{ %s; } 2>&1" % command, "r")
-      for line in f:
-          output.write(line)
-      status = f.close()
-      if status is None:
-          status = 0
-    else:
-          status = 0
-
-    if status == 0 and upgrade_testable(config, package, packages_files):
+    if ret == 0 and upgrade_testable(config, package, packages_files):
         distros = config["upgrade-test-distros"].split()
         distros = ["-d " + distro.strip() for distro in distros]
         distros = " ".join(distros)
         command = "%(piuparts-cmd)s -ab %(upgrade-test-chroot-tgz)s " % config
         command += distros
-
         if config["mirror"]:
           command += " --mirror %s " % config["mirror"]
-
         command += " " + package["Package"]
 
-        logging.debug("Executing: %s" % command)
-        output.write("\nExecuting: %s\n" % command)
-        f = os.popen("{ %s; } 2>&1" % command, "r")
-        for line in f:
-            output.write(line)
+        output.write("Executing: %s\n" % cmd)
+        ret,f = run_test_with_timeout(command, MAX_WAIT_TEST_RUN, True)
+        if ret < 0:
+            output.write(" *** Process KILLED - exceed maximum run time ***\n")
+        else:
+            output.write(f)
             output.flush()
-        status = f.close()
-        if status is None:
-            status = 0
 
     output.write("\n")
     output.write(time.strftime("End: %Y-%m-%d %H:%M:%S %Z\n", 
                                time.gmtime()))
     output.close()
-    if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
+    if ret != 0:
         subdir = "fail"
     else:
         subdir = "pass"

Reply via email to