Upon sending signals, ESRCH can be reported when the target no longer exists.
Signed-off-by: Michael Hanselmann <[email protected]> --- daemons/import-export | 2 +- lib/backend.py | 2 +- lib/impexpd/__init__.py | 1 + lib/utils.py | 23 +++++++++++++++++++++-- test/ganeti.utils_unittest.py | 22 ++++++++++++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/daemons/import-export b/daemons/import-export index 870f9a0..f7ebd4d 100755 --- a/daemons/import-export +++ b/daemons/import-export @@ -459,7 +459,7 @@ class ChildProcess(subprocess.Popen): """ logging.info("Sending signal %s to child process", signum) - os.killpg(self.pid, signum) + utils.IgnoreProcessNotFound(os.killpg, self.pid, signum) def ForceQuit(self): """Ensure child process is no longer running. diff --git a/lib/backend.py b/lib/backend.py index edf6518..c307619 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -2764,7 +2764,7 @@ def AbortImportExport(name): if pid: logging.info("Import/export %s is running with PID %s, sending SIGTERM", name, pid) - os.kill(pid, signal.SIGTERM) + utils.IgnoreProcessNotFound(os.kill, pid, signal.SIGTERM) def CleanupImportExport(name): diff --git a/lib/impexpd/__init__.py b/lib/impexpd/__init__.py index bcd4dab..02b7bb9 100644 --- a/lib/impexpd/__init__.py +++ b/lib/impexpd/__init__.py @@ -344,6 +344,7 @@ class ChildIOProcessor(object): raise # Process no longer exists + logging.debug("dd exited") self._dd_pid = None return True diff --git a/lib/utils.py b/lib/utils.py index 1ef35cb..49b5bc9 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -2339,8 +2339,7 @@ def KillProcess(pid, signal_=signal.SIGTERM, timeout=30, """ def _helper(pid, signal_, wait): """Simple helper to encapsulate the kill/waitpid sequence""" - os.kill(pid, signal_) - if wait: + if IgnoreProcessNotFound(os.kill, pid, signal_) and wait: try: os.waitpid(pid, os.WNOHANG) except OSError: @@ -3119,6 +3118,26 @@ def RunInSeparateProcess(fn, *args): return bool(exitcode) +def IgnoreProcessNotFound(fn, *args, **kwargs): + """Ignores ESRCH when calling a process-related function. + + ESRCH is raised when a process is not found. + + @rtype: bool + @return: Whether process was found + + """ + try: + fn(*args, **kwargs) + except EnvironmentError, err: + # Ignore ESRCH + if err.errno == errno.ESRCH: + return False + raise + + return True + + def IgnoreSignals(fn, *args, **kwargs): """Tries to call a function ignoring failures due to EINTR. diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 68ef826..9955d3b 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -2386,5 +2386,27 @@ class TestFormatSeconds(unittest.TestCase): self.assertEqual(utils.FormatSeconds(3912.8), "1h 5m 13s") +class RunIgnoreProcessNotFound(unittest.TestCase): + @staticmethod + def _WritePid(fd): + os.write(fd, str(os.getpid())) + os.close(fd) + return True + + def test(self): + (pid_read_fd, pid_write_fd) = os.pipe() + + # Start short-lived process which writes its PID to pipe + self.assert_(utils.RunInSeparateProcess(self._WritePid, pid_write_fd)) + os.close(pid_write_fd) + + # Read PID from pipe + pid = int(os.read(pid_read_fd, 1024)) + os.close(pid_read_fd) + + # Try to send signal to process which exited recently + self.assertFalse(utils.IgnoreProcessNotFound(os.kill, pid, 0)) + + if __name__ == '__main__': testutils.GanetiTestProgram() -- 1.7.0.4
