From 7831fc5fbe3a80d590a9215b73e58ed259bd0582 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Mon, 10 Jan 2011 17:51:46 +0100
Subject: [PATCH] utils: Move wrappers into separate file

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 Makefile.am                           |   4 +-
 lib/utils/__init__.py                 | 140 +--------------------
 lib/utils/wrapper.py                  | 170 ++++++++++++++++++++++++++
 test/ganeti.utils.wrapper_unittest.py | 126 +++++++++++++++++++
 test/ganeti.utils_unittest.py         |  90 +-------------
 5 files changed, 301 insertions(+), 229 deletions(-)
 create mode 100644 lib/utils/wrapper.py
 create mode 100755 test/ganeti.utils.wrapper_unittest.py

diff --git a/Makefile.am b/Makefile.am
index 7704dd97f..bbec35280 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -218,7 +218,8 @@ utils_PYTHON = \
 	lib/utils/log.py \
 	lib/utils/mlock.py \
 	lib/utils/retry.py \
-	lib/utils/text.py
+	lib/utils/text.py \
+	lib/utils/wrapper.py
 
 docrst = \
 	doc/admin.rst \
@@ -489,6 +490,7 @@ python_tests = \
 	test/ganeti.utils.mlock_unittest.py \
 	test/ganeti.utils.retry_unittest.py \
 	test/ganeti.utils.text_unittest.py \
+	test/ganeti.utils.wrapper_unittest.py \
 	test/ganeti.utils_unittest.py \
 	test/ganeti.workerpool_unittest.py \
 	test/cfgupgrade_unittest.py \
diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py
index 061456fa0..eb8d1b028 100644
--- a/lib/utils/__init__.py
+++ b/lib/utils/__init__.py
@@ -59,6 +59,7 @@ from ganeti.utils.text import * # pylint: disable-msg=W0401
 from ganeti.utils.mlock import * # pylint: disable-msg=W0401
 from ganeti.utils.log import * # pylint: disable-msg=W0401
 from ganeti.utils.hash import * # pylint: disable-msg=W0401
+from ganeti.utils.wrapper import * # pylint: disable-msg=W0401
 
 
 #: when set to True, L{RunCmd} is disabled
@@ -662,61 +663,6 @@ def _RunCmdFile(cmd, env, via_shell, output, cwd):
   return status
 
 
-def SetCloseOnExecFlag(fd, enable):
-  """Sets or unsets the close-on-exec flag on a file descriptor.
-
-  @type fd: int
-  @param fd: File descriptor
-  @type enable: bool
-  @param enable: Whether to set or unset it.
-
-  """
-  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
-
-  if enable:
-    flags |= fcntl.FD_CLOEXEC
-  else:
-    flags &= ~fcntl.FD_CLOEXEC
-
-  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
-
-
-def SetNonblockFlag(fd, enable):
-  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
-
-  @type fd: int
-  @param fd: File descriptor
-  @type enable: bool
-  @param enable: Whether to set or unset it
-
-  """
-  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
-
-  if enable:
-    flags |= os.O_NONBLOCK
-  else:
-    flags &= ~os.O_NONBLOCK
-
-  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
-
-
-def RetryOnSignal(fn, *args, **kwargs):
-  """Calls a function again if it failed due to EINTR.
-
-  """
-  while True:
-    try:
-      return fn(*args, **kwargs)
-    except EnvironmentError, err:
-      if err.errno != errno.EINTR:
-        raise
-    except (socket.error, select.error), err:
-      # In python 2.6 and above select.error is an IOError, so it's handled
-      # above, in 2.5 and below it's not, and it's handled here.
-      if not (err.args and err.args[0] == errno.EINTR):
-        raise
-
-
 def RunParts(dir_name, env=None, reset_env=False):
   """Run Scripts or programs in a directory
 
@@ -1876,41 +1822,6 @@ def WaitForFdCondition(fdobj, event, timeout):
   return result
 
 
-def TestDelay(duration):
-  """Sleep for a fixed amount of time.
-
-  @type duration: float
-  @param duration: the sleep duration
-  @rtype: boolean
-  @return: False for negative value, True otherwise
-
-  """
-  if duration < 0:
-    return False, "Invalid sleep duration"
-  time.sleep(duration)
-  return True, None
-
-
-def CloseFdNoError(fd, retries=5):
-  """Close a file descriptor ignoring errors.
-
-  @type fd: int
-  @param fd: the file descriptor
-  @type retries: int
-  @param retries: how many retries to make, in case we get any
-      other error than EBADF
-
-  """
-  try:
-    os.close(fd)
-  except OSError, err:
-    if err.errno != errno.EBADF:
-      if retries > 0:
-        CloseFdNoError(fd, retries - 1)
-    # else either it's closed already or we're out of retries, so we
-    # ignore this and go on
-
-
 def CloseFDs(noclose_fds=None):
   """Close file descriptors.
 
@@ -2638,46 +2549,6 @@ 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.
-
-  """
-  try:
-    return fn(*args, **kwargs)
-  except EnvironmentError, err:
-    if err.errno == errno.EINTR:
-      return None
-    else:
-      raise
-  except (select.error, socket.error), err:
-    # In python 2.6 and above select.error is an IOError, so it's handled
-    # above, in 2.5 and below it's not, and it's handled here.
-    if err.args and err.args[0] == errno.EINTR:
-      return None
-    else:
-      raise
-
-
 def LockFile(fd):
   """Locks a file using POSIX locks.
 
@@ -2736,15 +2607,6 @@ def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
   return value
 
 
-def GetClosedTempfile(*args, **kwargs):
-  """Creates a temporary file and returns its path.
-
-  """
-  (fd, path) = tempfile.mkstemp(*args, **kwargs)
-  CloseFdNoError(fd)
-  return path
-
-
 def GenerateSelfSignedX509Cert(common_name, validity):
   """Generates a self-signed X509 certificate.
 
diff --git a/lib/utils/wrapper.py b/lib/utils/wrapper.py
new file mode 100644
index 000000000..b87f69c35
--- /dev/null
+++ b/lib/utils/wrapper.py
@@ -0,0 +1,170 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+"""Utility functions wrapping other functions.
+
+"""
+
+import time
+import socket
+import errno
+import tempfile
+import fcntl
+import os
+import select
+
+
+def TestDelay(duration):
+  """Sleep for a fixed amount of time.
+
+  @type duration: float
+  @param duration: the sleep duration
+  @rtype: boolean
+  @return: False for negative value, True otherwise
+
+  """
+  if duration < 0:
+    return False, "Invalid sleep duration"
+  time.sleep(duration)
+  return True, None
+
+
+def CloseFdNoError(fd, retries=5):
+  """Close a file descriptor ignoring errors.
+
+  @type fd: int
+  @param fd: the file descriptor
+  @type retries: int
+  @param retries: how many retries to make, in case we get any
+      other error than EBADF
+
+  """
+  try:
+    os.close(fd)
+  except OSError, err:
+    if err.errno != errno.EBADF:
+      if retries > 0:
+        CloseFdNoError(fd, retries - 1)
+    # else either it's closed already or we're out of retries, so we
+    # ignore this and go on
+
+
+def SetCloseOnExecFlag(fd, enable):
+  """Sets or unsets the close-on-exec flag on a file descriptor.
+
+  @type fd: int
+  @param fd: File descriptor
+  @type enable: bool
+  @param enable: Whether to set or unset it.
+
+  """
+  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
+
+  if enable:
+    flags |= fcntl.FD_CLOEXEC
+  else:
+    flags &= ~fcntl.FD_CLOEXEC
+
+  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
+
+
+def SetNonblockFlag(fd, enable):
+  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
+
+  @type fd: int
+  @param fd: File descriptor
+  @type enable: bool
+  @param enable: Whether to set or unset it
+
+  """
+  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
+
+  if enable:
+    flags |= os.O_NONBLOCK
+  else:
+    flags &= ~os.O_NONBLOCK
+
+  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
+
+
+def RetryOnSignal(fn, *args, **kwargs):
+  """Calls a function again if it failed due to EINTR.
+
+  """
+  while True:
+    try:
+      return fn(*args, **kwargs)
+    except EnvironmentError, err:
+      if err.errno != errno.EINTR:
+        raise
+    except (socket.error, select.error), err:
+      # In python 2.6 and above select.error is an IOError, so it's handled
+      # above, in 2.5 and below it's not, and it's handled here.
+      if not (err.args and err.args[0] == errno.EINTR):
+        raise
+
+
+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.
+
+  """
+  try:
+    return fn(*args, **kwargs)
+  except EnvironmentError, err:
+    if err.errno == errno.EINTR:
+      return None
+    else:
+      raise
+  except (select.error, socket.error), err:
+    # In python 2.6 and above select.error is an IOError, so it's handled
+    # above, in 2.5 and below it's not, and it's handled here.
+    if err.args and err.args[0] == errno.EINTR:
+      return None
+    else:
+      raise
+
+
+def GetClosedTempfile(*args, **kwargs):
+  """Creates a temporary file and returns its path.
+
+  """
+  (fd, path) = tempfile.mkstemp(*args, **kwargs)
+  CloseFdNoError(fd)
+  return path
diff --git a/test/ganeti.utils.wrapper_unittest.py b/test/ganeti.utils.wrapper_unittest.py
new file mode 100755
index 000000000..16633f7a2
--- /dev/null
+++ b/test/ganeti.utils.wrapper_unittest.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for testing ganeti.utils.wrapper"""
+
+import errno
+import fcntl
+import os
+import socket
+import tempfile
+import unittest
+
+from ganeti import constants
+from ganeti import utils
+
+import testutils
+
+
+class TestSetCloseOnExecFlag(unittest.TestCase):
+  """Tests for SetCloseOnExecFlag"""
+
+  def setUp(self):
+    self.tmpfile = tempfile.TemporaryFile()
+
+  def testEnable(self):
+    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), True)
+    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
+                    fcntl.FD_CLOEXEC)
+
+  def testDisable(self):
+    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), False)
+    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
+                fcntl.FD_CLOEXEC)
+
+
+class TestSetNonblockFlag(unittest.TestCase):
+  def setUp(self):
+    self.tmpfile = tempfile.TemporaryFile()
+
+  def testEnable(self):
+    utils.SetNonblockFlag(self.tmpfile.fileno(), True)
+    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
+                    os.O_NONBLOCK)
+
+  def testDisable(self):
+    utils.SetNonblockFlag(self.tmpfile.fileno(), False)
+    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
+                os.O_NONBLOCK)
+
+
+class TestIgnoreProcessNotFound(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))
+
+
+class TestIgnoreSignals(unittest.TestCase):
+  """Test the IgnoreSignals decorator"""
+
+  @staticmethod
+  def _Raise(exception):
+    raise exception
+
+  @staticmethod
+  def _Return(rval):
+    return rval
+
+  def testIgnoreSignals(self):
+    sock_err_intr = socket.error(errno.EINTR, "Message")
+    sock_err_inval = socket.error(errno.EINVAL, "Message")
+
+    env_err_intr = EnvironmentError(errno.EINTR, "Message")
+    env_err_inval = EnvironmentError(errno.EINVAL, "Message")
+
+    self.assertRaises(socket.error, self._Raise, sock_err_intr)
+    self.assertRaises(socket.error, self._Raise, sock_err_inval)
+    self.assertRaises(EnvironmentError, self._Raise, env_err_intr)
+    self.assertRaises(EnvironmentError, self._Raise, env_err_inval)
+
+    self.assertEquals(utils.IgnoreSignals(self._Raise, sock_err_intr), None)
+    self.assertEquals(utils.IgnoreSignals(self._Raise, env_err_intr), None)
+    self.assertRaises(socket.error, utils.IgnoreSignals, self._Raise,
+                      sock_err_inval)
+    self.assertRaises(EnvironmentError, utils.IgnoreSignals, self._Raise,
+                      env_err_inval)
+
+    self.assertEquals(utils.IgnoreSignals(self._Return, True), True)
+    self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
+
+
+if __name__ == "__main__":
+  testutils.GanetiTestProgram()
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index a62feb52b..baaca37be 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2006, 2007, 2010 Google Inc.
+# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -627,38 +627,6 @@ class TestStartDaemon(testutils.GanetiTestCase):
       os.close(fd)
 
 
-class TestSetCloseOnExecFlag(unittest.TestCase):
-  """Tests for SetCloseOnExecFlag"""
-
-  def setUp(self):
-    self.tmpfile = tempfile.TemporaryFile()
-
-  def testEnable(self):
-    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), True)
-    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
-                    fcntl.FD_CLOEXEC)
-
-  def testDisable(self):
-    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), False)
-    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
-                fcntl.FD_CLOEXEC)
-
-
-class TestSetNonblockFlag(unittest.TestCase):
-  def setUp(self):
-    self.tmpfile = tempfile.TemporaryFile()
-
-  def testEnable(self):
-    utils.SetNonblockFlag(self.tmpfile.fileno(), True)
-    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
-                    os.O_NONBLOCK)
-
-  def testDisable(self):
-    utils.SetNonblockFlag(self.tmpfile.fileno(), False)
-    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
-                os.O_NONBLOCK)
-
-
 class TestRemoveFile(unittest.TestCase):
   """Test case for the RemoveFile function"""
 
@@ -1801,40 +1769,6 @@ class TestVerifyCertificateInner(unittest.TestCase):
     self.assertEqual(errcode, utils.CERT_ERROR)
 
 
-class TestIgnoreSignals(unittest.TestCase):
-  """Test the IgnoreSignals decorator"""
-
-  @staticmethod
-  def _Raise(exception):
-    raise exception
-
-  @staticmethod
-  def _Return(rval):
-    return rval
-
-  def testIgnoreSignals(self):
-    sock_err_intr = socket.error(errno.EINTR, "Message")
-    sock_err_inval = socket.error(errno.EINVAL, "Message")
-
-    env_err_intr = EnvironmentError(errno.EINTR, "Message")
-    env_err_inval = EnvironmentError(errno.EINVAL, "Message")
-
-    self.assertRaises(socket.error, self._Raise, sock_err_intr)
-    self.assertRaises(socket.error, self._Raise, sock_err_inval)
-    self.assertRaises(EnvironmentError, self._Raise, env_err_intr)
-    self.assertRaises(EnvironmentError, self._Raise, env_err_inval)
-
-    self.assertEquals(utils.IgnoreSignals(self._Raise, sock_err_intr), None)
-    self.assertEquals(utils.IgnoreSignals(self._Raise, env_err_intr), None)
-    self.assertRaises(socket.error, utils.IgnoreSignals, self._Raise,
-                      sock_err_inval)
-    self.assertRaises(EnvironmentError, utils.IgnoreSignals, self._Raise,
-                      env_err_inval)
-
-    self.assertEquals(utils.IgnoreSignals(self._Return, True), True)
-    self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
-
-
 class TestEnsureDirs(unittest.TestCase):
   """Tests for EnsureDirs"""
 
@@ -1857,28 +1791,6 @@ class TestEnsureDirs(unittest.TestCase):
     os.umask(self.old_umask)
 
 
-class TestIgnoreProcessNotFound(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))
-
-
 class TestFindMatch(unittest.TestCase):
   def test(self):
     data = {
-- 
GitLab