From: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
We need a common function to execute shell command.
We also need timeout when execute shell command.
A threading.Timer is used to send signal.SIGKILL to kill the command
when timeout.
run a command with timeout as follow.
$ PYTHONPATH=src python -c '
from kimchi import utils
print utils.run_command(["dd", "if=/dev/zero",
"of=/dev/null"], 1.5)
'
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Signed-off-by: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
---
src/kimchi/exception.py | 4 ++++
src/kimchi/utils.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py
index bff0a18..a37015b 100644
--- a/src/kimchi/exception.py
+++ b/src/kimchi/exception.py
@@ -43,3 +43,7 @@ class InvalidOperation(Exception):
class IsoFormatError(Exception):
pass
+
+
+class TimeoutExpired(Exception):
+ pass
diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py
index af245c6..331da91 100644
--- a/src/kimchi/utils.py
+++ b/src/kimchi/utils.py
@@ -23,13 +23,16 @@
import cherrypy
import os
+import subprocess
import urllib2
from cherrypy.lib.reprconf import Parser
+from kimchi.exception import TimeoutExpired
from kimchi import config
+from threading import Timer
kimchi_log = cherrypy.log.error_log
@@ -96,3 +99,58 @@ def check_url_path(path):
return False
return True
+
+
+def run_command(cmd, timeout=None):
+ """
+ cmd is a sequence of command arguments.
+ timeout is a float number in seconds.
+ timeout default value is None, means command run without timeout.
+ """
+ def kill_proc(proc, timeout_flag):
+ try:
+ proc.kill()
+ except OSError:
+ pass
+ else:
+ timeout_flag[0] = True
+
+ proc = None
+ timer = None
+ timeout_flag = [False]
+
+ try:
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ if timeout is not None:
+ timer = Timer(timeout, kill_proc, [proc, timeout_flag])
+ timer.setDaemon(True)
+ timer.start()
+
+ out, error = proc.communicate()
+ kimchi_log.debug("Run command: '%s'", " ".join(cmd))
+
+ if out or error:
+ kimchi_log.debug("out:\n %s\nerror:\n %s", out, error)
+
+ if timeout_flag[0]:
+ msg = ("subprocess is killed by signal.SIGKILL for "
+ "timeout %s seconds" % timeout)
+ kimchi_log.error(msg)
+ raise TimeoutExpired(msg)
+
+ return out, error, proc.returncode
+ except TimeoutExpired:
+ raise
+ except Exception as e:
+ msg = "Failed to run command: %s." % " ".join(cmd)
+ msg = msg if proc is None else msg + "\n error code: %s."
+ kimchi_log.error("%s\n %s", msg, e)
+
+ if proc:
+ return out, error, proc.returncode
+ else:
+ return None, None, None
+ finally:
+ if timer and not timeout_flag[0]:
+ timer.cancel()
--
1.8.4.2