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

Reply via email to