From debed9aefaba1cc98bc05e908e2ee85f85eea304 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Tue, 20 Apr 2010 18:18:06 +0200
Subject: [PATCH] utils: Add function to read locked PID file

This is useful in combination with utils.StartDaemon and will be used for
reading the import/export daemon's PID file.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
---
 lib/backend.py                | 19 +-----------------
 lib/utils.py                  | 31 +++++++++++++++++++++++++++++
 test/ganeti.utils_unittest.py | 37 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 18 deletions(-)

diff --git a/lib/backend.py b/lib/backend.py
index beeca61c2..69ac800c9 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -2855,25 +2855,8 @@ def CleanupImportExport(name):
   logging.info("Finalizing import/export %s", name)
 
   status_dir = utils.PathJoin(constants.IMPORT_EXPORT_DIR, name)
-  pid_file = utils.PathJoin(status_dir, _IES_PID_FILE)
 
-  pid = None
-  try:
-    fd = os.open(pid_file, os.O_RDONLY)
-  except EnvironmentError, err:
-    if err.errno != errno.ENOENT:
-      raise
-    # PID file doesn't exist
-  else:
-    try:
-      try:
-        # Try to acquire lock
-        utils.LockFile(fd)
-      except errors.LockError:
-        # Couldn't lock, daemon is running
-        pid = int(os.read(fd, 100))
-    finally:
-      os.close(fd)
+  pid = utils.ReadLockedPidFile(utils.PathJoin(status_dir, _IES_PID_FILE))
 
   if pid:
     logging.info("Import/export %s is still running with PID %s",
diff --git a/lib/utils.py b/lib/utils.py
index 9076558a3..48b427ac3 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -840,6 +840,37 @@ def ReadPidFile(pidfile):
   return pid
 
 
+def ReadLockedPidFile(path):
+  """Reads a locked PID file.
+
+  This can be used together with L{StartDaemon}.
+
+  @type path: string
+  @param path: Path to PID file
+  @return: PID as integer or, if file was unlocked or couldn't be opened, None
+
+  """
+  try:
+    fd = os.open(path, os.O_RDONLY)
+  except EnvironmentError, err:
+    if err.errno == errno.ENOENT:
+      # PID file doesn't exist
+      return None
+    raise
+
+  try:
+    try:
+      # Try to acquire lock
+      LockFile(fd)
+    except errors.LockError:
+      # Couldn't lock, daemon is running
+      return int(os.read(fd, 100))
+  finally:
+    os.close(fd)
+
+  return None
+
+
 def MatchNameComponent(key, name_list, case_sensitive=True):
   """Try to match a name against a list.
 
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index 2d8a79417..7fc48a963 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -1840,5 +1840,42 @@ class TestLineSplitter(unittest.TestCase):
                              "", "x"])
 
 
+class TestReadLockedPidFile(unittest.TestCase):
+  def setUp(self):
+    self.tmpdir = tempfile.mkdtemp()
+
+  def tearDown(self):
+    shutil.rmtree(self.tmpdir)
+
+  def testNonExistent(self):
+    path = utils.PathJoin(self.tmpdir, "nonexist")
+    self.assert_(utils.ReadLockedPidFile(path) is None)
+
+  def testUnlocked(self):
+    path = utils.PathJoin(self.tmpdir, "pid")
+    utils.WriteFile(path, data="123")
+    self.assert_(utils.ReadLockedPidFile(path) is None)
+
+  def testLocked(self):
+    path = utils.PathJoin(self.tmpdir, "pid")
+    utils.WriteFile(path, data="123")
+
+    fl = utils.FileLock.Open(path)
+    try:
+      fl.Exclusive(blocking=True)
+
+      self.assertEqual(utils.ReadLockedPidFile(path), 123)
+    finally:
+      fl.Close()
+
+    self.assert_(utils.ReadLockedPidFile(path) is None)
+
+  def testError(self):
+    path = utils.PathJoin(self.tmpdir, "foobar", "pid")
+    utils.WriteFile(utils.PathJoin(self.tmpdir, "foobar"), data="")
+    # open(2) should return ENOTDIR
+    self.assertRaises(EnvironmentError, utils.ReadLockedPidFile, path)
+
+
 if __name__ == '__main__':
   testutils.GanetiTestProgram()
-- 
GitLab