Commit b2a1f511 authored by Iustin Pop's avatar Iustin Pop
Browse files

Add a KillProcess function

We cannot depend on all environments to have a start-stop-daemon or
similar tool. We instead implement a KillProcess function that behaves
similar to “start-stop-daemon --retry”.

Note that the attached unittest can hang in foreground if the child
misbehaves (doesn't write to the internal pipe). Since unittest are
either run in the foreground or are run with a timeout from an automated
framework, I think this is an acceptable trade-off (against of using
hardcoded timeouts in the test).

Reviewed-by: imsnah
parent d9f311d7
......@@ -1092,6 +1092,35 @@ def RemovePidFile(name):
pass
def KillProcess(pid, signal=signal.SIGTERM, timeout=30):
"""Kill a process given by its pid.
@type pid: int
@param pid: The PID to terminate.
@type signal: int
@param signal: The signal to send, by default SIGTERM
@type timeout: int
@param timeout: The timeout after which, if the process is still alive,
a SIGKILL will be sent. If not positive, no such checking
will be done
"""
if pid <= 0:
# kill with pid=0 == suicide
raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
if not IsProcessAlive(pid):
return
os.kill(pid, signal)
if timeout <= 0:
return
end = time.time() + timeout
while time.time() < end and IsProcessAlive(pid):
time.sleep(0.1)
if IsProcessAlive(pid):
os.kill(pid, signal.SIGKILL)
def FindFile(name, search_path, test=os.path.exists):
"""Look for a filesystem object in a given path.
......
......@@ -43,7 +43,8 @@ from ganeti.utils import IsProcessAlive, RunCmd, \
ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree
from ganeti.errors import LockError, UnitParseError, GenericError
from ganeti.errors import LockError, UnitParseError, GenericError, \
ProgrammerError
def _ChildHandler(signal, stack):
global _ChildFlag
......@@ -132,6 +133,27 @@ class TestPidFileFunctions(unittest.TestCase):
self.failIf(os.path.exists(pid_file),
"PID file should not exist anymore")
def testKill(self):
pid_file = self.f_dpn('child')
r_fd, w_fd = os.pipe()
new_pid = os.fork()
if new_pid == 0: #child
utils.WritePidFile('child')
os.write(w_fd, 'a')
signal.pause()
os._exit(0)
return
# else we are in the parent
# wait until the child has written the pid file
os.read(r_fd, 1)
read_pid = utils.ReadPidFile(pid_file)
self.failUnlessEqual(read_pid, new_pid)
self.failUnless(utils.IsProcessAlive(new_pid))
utils.KillProcess(new_pid)
self.failIf(utils.IsProcessAlive(new_pid))
utils.RemovePidFile('child')
self.failUnlessRaises(ProgrammerError, utils.KillProcess, 0)
def tearDown(self):
for name in os.listdir(self.dir):
os.unlink(os.path.join(self.dir, name))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment