[PATCH V3] add a synchronous function with timeout to execute command

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V2 -> V3 address zhengsheng's commnet. raise TimeoutExpired when timeout do not return timeout_flag V1 -> V2 check timeout happens and report a specific error + if proc.returncode == -9 and timeout_flag[0] is True: + kimchi_log.error("subprocess is killed by signal.SIGKILL " + "for timeout %s seconds", timeout) ShaoHe Feng (1): add a synchronous function with timeout to execute command src/kimchi/exception.py | 4 ++++ src/kimchi/utils.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) -- 1.8.4.2

From: ShaoHe Feng <shaohef@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@linux.vnet.ibm.com> Signed-off-by: ShaoHe Feng <shaohef@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

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 01/14/2014 11:03 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@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@linux.vnet.ibm.com> Signed-off-by: ShaoHe Feng <shaohef@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()

Hi, aline. server patches depend on this patch. On 01/14/2014 09:03 PM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
V2 -> V3 address zhengsheng's commnet. raise TimeoutExpired when timeout do not return timeout_flag
V1 -> V2 check timeout happens and report a specific error + if proc.returncode == -9 and timeout_flag[0] is True: + kimchi_log.error("subprocess is killed by signal.SIGKILL " + "for timeout %s seconds", timeout)
ShaoHe Feng (1): add a synchronous function with timeout to execute command
src/kimchi/exception.py | 4 ++++ src/kimchi/utils.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+)
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 01/14/2014 09:03 PM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
V2 -> V3 address zhengsheng's commnet. raise TimeoutExpired when timeout do not return timeout_flag
V1 -> V2 check timeout happens and report a specific error + if proc.returncode == -9 and timeout_flag[0] is True: + kimchi_log.error("subprocess is killed by signal.SIGKILL " + "for timeout %s seconds", timeout)
ShaoHe Feng (1): add a synchronous function with timeout to execute command
src/kimchi/exception.py | 4 ++++ src/kimchi/utils.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+)
ping -- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center
participants (5)
-
Aline Manera
-
Ramon Medeiros
-
Rodrigo Trujillo
-
shaohef@linux.vnet.ibm.com
-
Sheldon