Revision 7831fc5f

b/Makefile.am
218 218
	lib/utils/log.py \
219 219
	lib/utils/mlock.py \
220 220
	lib/utils/retry.py \
221
	lib/utils/text.py
221
	lib/utils/text.py \
222
	lib/utils/wrapper.py
222 223

  
223 224
docrst = \
224 225
	doc/admin.rst \
......
489 490
	test/ganeti.utils.mlock_unittest.py \
490 491
	test/ganeti.utils.retry_unittest.py \
491 492
	test/ganeti.utils.text_unittest.py \
493
	test/ganeti.utils.wrapper_unittest.py \
492 494
	test/ganeti.utils_unittest.py \
493 495
	test/ganeti.workerpool_unittest.py \
494 496
	test/cfgupgrade_unittest.py \
b/lib/utils/__init__.py
59 59
from ganeti.utils.mlock import * # pylint: disable-msg=W0401
60 60
from ganeti.utils.log import * # pylint: disable-msg=W0401
61 61
from ganeti.utils.hash import * # pylint: disable-msg=W0401
62
from ganeti.utils.wrapper import * # pylint: disable-msg=W0401
62 63

  
63 64

  
64 65
#: when set to True, L{RunCmd} is disabled
......
662 663
  return status
663 664

  
664 665

  
665
def SetCloseOnExecFlag(fd, enable):
666
  """Sets or unsets the close-on-exec flag on a file descriptor.
667

  
668
  @type fd: int
669
  @param fd: File descriptor
670
  @type enable: bool
671
  @param enable: Whether to set or unset it.
672

  
673
  """
674
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
675

  
676
  if enable:
677
    flags |= fcntl.FD_CLOEXEC
678
  else:
679
    flags &= ~fcntl.FD_CLOEXEC
680

  
681
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
682

  
683

  
684
def SetNonblockFlag(fd, enable):
685
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
686

  
687
  @type fd: int
688
  @param fd: File descriptor
689
  @type enable: bool
690
  @param enable: Whether to set or unset it
691

  
692
  """
693
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
694

  
695
  if enable:
696
    flags |= os.O_NONBLOCK
697
  else:
698
    flags &= ~os.O_NONBLOCK
699

  
700
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
701

  
702

  
703
def RetryOnSignal(fn, *args, **kwargs):
704
  """Calls a function again if it failed due to EINTR.
705

  
706
  """
707
  while True:
708
    try:
709
      return fn(*args, **kwargs)
710
    except EnvironmentError, err:
711
      if err.errno != errno.EINTR:
712
        raise
713
    except (socket.error, select.error), err:
714
      # In python 2.6 and above select.error is an IOError, so it's handled
715
      # above, in 2.5 and below it's not, and it's handled here.
716
      if not (err.args and err.args[0] == errno.EINTR):
717
        raise
718

  
719

  
720 666
def RunParts(dir_name, env=None, reset_env=False):
721 667
  """Run Scripts or programs in a directory
722 668

  
......
1876 1822
  return result
1877 1823

  
1878 1824

  
1879
def TestDelay(duration):
1880
  """Sleep for a fixed amount of time.
1881

  
1882
  @type duration: float
1883
  @param duration: the sleep duration
1884
  @rtype: boolean
1885
  @return: False for negative value, True otherwise
1886

  
1887
  """
1888
  if duration < 0:
1889
    return False, "Invalid sleep duration"
1890
  time.sleep(duration)
1891
  return True, None
1892

  
1893

  
1894
def CloseFdNoError(fd, retries=5):
1895
  """Close a file descriptor ignoring errors.
1896

  
1897
  @type fd: int
1898
  @param fd: the file descriptor
1899
  @type retries: int
1900
  @param retries: how many retries to make, in case we get any
1901
      other error than EBADF
1902

  
1903
  """
1904
  try:
1905
    os.close(fd)
1906
  except OSError, err:
1907
    if err.errno != errno.EBADF:
1908
      if retries > 0:
1909
        CloseFdNoError(fd, retries - 1)
1910
    # else either it's closed already or we're out of retries, so we
1911
    # ignore this and go on
1912

  
1913

  
1914 1825
def CloseFDs(noclose_fds=None):
1915 1826
  """Close file descriptors.
1916 1827

  
......
2638 2549
  return bool(exitcode)
2639 2550

  
2640 2551

  
2641
def IgnoreProcessNotFound(fn, *args, **kwargs):
2642
  """Ignores ESRCH when calling a process-related function.
2643

  
2644
  ESRCH is raised when a process is not found.
2645

  
2646
  @rtype: bool
2647
  @return: Whether process was found
2648

  
2649
  """
2650
  try:
2651
    fn(*args, **kwargs)
2652
  except EnvironmentError, err:
2653
    # Ignore ESRCH
2654
    if err.errno == errno.ESRCH:
2655
      return False
2656
    raise
2657

  
2658
  return True
2659

  
2660

  
2661
def IgnoreSignals(fn, *args, **kwargs):
2662
  """Tries to call a function ignoring failures due to EINTR.
2663

  
2664
  """
2665
  try:
2666
    return fn(*args, **kwargs)
2667
  except EnvironmentError, err:
2668
    if err.errno == errno.EINTR:
2669
      return None
2670
    else:
2671
      raise
2672
  except (select.error, socket.error), err:
2673
    # In python 2.6 and above select.error is an IOError, so it's handled
2674
    # above, in 2.5 and below it's not, and it's handled here.
2675
    if err.args and err.args[0] == errno.EINTR:
2676
      return None
2677
    else:
2678
      raise
2679

  
2680

  
2681 2552
def LockFile(fd):
2682 2553
  """Locks a file using POSIX locks.
2683 2554

  
......
2736 2607
  return value
2737 2608

  
2738 2609

  
2739
def GetClosedTempfile(*args, **kwargs):
2740
  """Creates a temporary file and returns its path.
2741

  
2742
  """
2743
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
2744
  CloseFdNoError(fd)
2745
  return path
2746

  
2747

  
2748 2610
def GenerateSelfSignedX509Cert(common_name, validity):
2749 2611
  """Generates a self-signed X509 certificate.
2750 2612

  
b/lib/utils/wrapper.py
1
#
2
#
3

  
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21
"""Utility functions wrapping other functions.
22

  
23
"""
24

  
25
import time
26
import socket
27
import errno
28
import tempfile
29
import fcntl
30
import os
31
import select
32

  
33

  
34
def TestDelay(duration):
35
  """Sleep for a fixed amount of time.
36

  
37
  @type duration: float
38
  @param duration: the sleep duration
39
  @rtype: boolean
40
  @return: False for negative value, True otherwise
41

  
42
  """
43
  if duration < 0:
44
    return False, "Invalid sleep duration"
45
  time.sleep(duration)
46
  return True, None
47

  
48

  
49
def CloseFdNoError(fd, retries=5):
50
  """Close a file descriptor ignoring errors.
51

  
52
  @type fd: int
53
  @param fd: the file descriptor
54
  @type retries: int
55
  @param retries: how many retries to make, in case we get any
56
      other error than EBADF
57

  
58
  """
59
  try:
60
    os.close(fd)
61
  except OSError, err:
62
    if err.errno != errno.EBADF:
63
      if retries > 0:
64
        CloseFdNoError(fd, retries - 1)
65
    # else either it's closed already or we're out of retries, so we
66
    # ignore this and go on
67

  
68

  
69
def SetCloseOnExecFlag(fd, enable):
70
  """Sets or unsets the close-on-exec flag on a file descriptor.
71

  
72
  @type fd: int
73
  @param fd: File descriptor
74
  @type enable: bool
75
  @param enable: Whether to set or unset it.
76

  
77
  """
78
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
79

  
80
  if enable:
81
    flags |= fcntl.FD_CLOEXEC
82
  else:
83
    flags &= ~fcntl.FD_CLOEXEC
84

  
85
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
86

  
87

  
88
def SetNonblockFlag(fd, enable):
89
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
90

  
91
  @type fd: int
92
  @param fd: File descriptor
93
  @type enable: bool
94
  @param enable: Whether to set or unset it
95

  
96
  """
97
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
98

  
99
  if enable:
100
    flags |= os.O_NONBLOCK
101
  else:
102
    flags &= ~os.O_NONBLOCK
103

  
104
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
105

  
106

  
107
def RetryOnSignal(fn, *args, **kwargs):
108
  """Calls a function again if it failed due to EINTR.
109

  
110
  """
111
  while True:
112
    try:
113
      return fn(*args, **kwargs)
114
    except EnvironmentError, err:
115
      if err.errno != errno.EINTR:
116
        raise
117
    except (socket.error, select.error), err:
118
      # In python 2.6 and above select.error is an IOError, so it's handled
119
      # above, in 2.5 and below it's not, and it's handled here.
120
      if not (err.args and err.args[0] == errno.EINTR):
121
        raise
122

  
123

  
124
def IgnoreProcessNotFound(fn, *args, **kwargs):
125
  """Ignores ESRCH when calling a process-related function.
126

  
127
  ESRCH is raised when a process is not found.
128

  
129
  @rtype: bool
130
  @return: Whether process was found
131

  
132
  """
133
  try:
134
    fn(*args, **kwargs)
135
  except EnvironmentError, err:
136
    # Ignore ESRCH
137
    if err.errno == errno.ESRCH:
138
      return False
139
    raise
140

  
141
  return True
142

  
143

  
144
def IgnoreSignals(fn, *args, **kwargs):
145
  """Tries to call a function ignoring failures due to EINTR.
146

  
147
  """
148
  try:
149
    return fn(*args, **kwargs)
150
  except EnvironmentError, err:
151
    if err.errno == errno.EINTR:
152
      return None
153
    else:
154
      raise
155
  except (select.error, socket.error), err:
156
    # In python 2.6 and above select.error is an IOError, so it's handled
157
    # above, in 2.5 and below it's not, and it's handled here.
158
    if err.args and err.args[0] == errno.EINTR:
159
      return None
160
    else:
161
      raise
162

  
163

  
164
def GetClosedTempfile(*args, **kwargs):
165
  """Creates a temporary file and returns its path.
166

  
167
  """
168
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
169
  CloseFdNoError(fd)
170
  return path
b/test/ganeti.utils.wrapper_unittest.py
1
#!/usr/bin/python
2
#
3

  
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21

  
22
"""Script for testing ganeti.utils.wrapper"""
23

  
24
import errno
25
import fcntl
26
import os
27
import socket
28
import tempfile
29
import unittest
30

  
31
from ganeti import constants
32
from ganeti import utils
33

  
34
import testutils
35

  
36

  
37
class TestSetCloseOnExecFlag(unittest.TestCase):
38
  """Tests for SetCloseOnExecFlag"""
39

  
40
  def setUp(self):
41
    self.tmpfile = tempfile.TemporaryFile()
42

  
43
  def testEnable(self):
44
    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), True)
45
    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
46
                    fcntl.FD_CLOEXEC)
47

  
48
  def testDisable(self):
49
    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), False)
50
    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
51
                fcntl.FD_CLOEXEC)
52

  
53

  
54
class TestSetNonblockFlag(unittest.TestCase):
55
  def setUp(self):
56
    self.tmpfile = tempfile.TemporaryFile()
57

  
58
  def testEnable(self):
59
    utils.SetNonblockFlag(self.tmpfile.fileno(), True)
60
    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
61
                    os.O_NONBLOCK)
62

  
63
  def testDisable(self):
64
    utils.SetNonblockFlag(self.tmpfile.fileno(), False)
65
    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
66
                os.O_NONBLOCK)
67

  
68

  
69
class TestIgnoreProcessNotFound(unittest.TestCase):
70
  @staticmethod
71
  def _WritePid(fd):
72
    os.write(fd, str(os.getpid()))
73
    os.close(fd)
74
    return True
75

  
76
  def test(self):
77
    (pid_read_fd, pid_write_fd) = os.pipe()
78

  
79
    # Start short-lived process which writes its PID to pipe
80
    self.assert_(utils.RunInSeparateProcess(self._WritePid, pid_write_fd))
81
    os.close(pid_write_fd)
82

  
83
    # Read PID from pipe
84
    pid = int(os.read(pid_read_fd, 1024))
85
    os.close(pid_read_fd)
86

  
87
    # Try to send signal to process which exited recently
88
    self.assertFalse(utils.IgnoreProcessNotFound(os.kill, pid, 0))
89

  
90

  
91
class TestIgnoreSignals(unittest.TestCase):
92
  """Test the IgnoreSignals decorator"""
93

  
94
  @staticmethod
95
  def _Raise(exception):
96
    raise exception
97

  
98
  @staticmethod
99
  def _Return(rval):
100
    return rval
101

  
102
  def testIgnoreSignals(self):
103
    sock_err_intr = socket.error(errno.EINTR, "Message")
104
    sock_err_inval = socket.error(errno.EINVAL, "Message")
105

  
106
    env_err_intr = EnvironmentError(errno.EINTR, "Message")
107
    env_err_inval = EnvironmentError(errno.EINVAL, "Message")
108

  
109
    self.assertRaises(socket.error, self._Raise, sock_err_intr)
110
    self.assertRaises(socket.error, self._Raise, sock_err_inval)
111
    self.assertRaises(EnvironmentError, self._Raise, env_err_intr)
112
    self.assertRaises(EnvironmentError, self._Raise, env_err_inval)
113

  
114
    self.assertEquals(utils.IgnoreSignals(self._Raise, sock_err_intr), None)
115
    self.assertEquals(utils.IgnoreSignals(self._Raise, env_err_intr), None)
116
    self.assertRaises(socket.error, utils.IgnoreSignals, self._Raise,
117
                      sock_err_inval)
118
    self.assertRaises(EnvironmentError, utils.IgnoreSignals, self._Raise,
119
                      env_err_inval)
120

  
121
    self.assertEquals(utils.IgnoreSignals(self._Return, True), True)
122
    self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
123

  
124

  
125
if __name__ == "__main__":
126
  testutils.GanetiTestProgram()
b/test/ganeti.utils_unittest.py
1 1
#!/usr/bin/python
2 2
#
3 3

  
4
# Copyright (C) 2006, 2007, 2010 Google Inc.
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
627 627
      os.close(fd)
628 628

  
629 629

  
630
class TestSetCloseOnExecFlag(unittest.TestCase):
631
  """Tests for SetCloseOnExecFlag"""
632

  
633
  def setUp(self):
634
    self.tmpfile = tempfile.TemporaryFile()
635

  
636
  def testEnable(self):
637
    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), True)
638
    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
639
                    fcntl.FD_CLOEXEC)
640

  
641
  def testDisable(self):
642
    utils.SetCloseOnExecFlag(self.tmpfile.fileno(), False)
643
    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFD) &
644
                fcntl.FD_CLOEXEC)
645

  
646

  
647
class TestSetNonblockFlag(unittest.TestCase):
648
  def setUp(self):
649
    self.tmpfile = tempfile.TemporaryFile()
650

  
651
  def testEnable(self):
652
    utils.SetNonblockFlag(self.tmpfile.fileno(), True)
653
    self.failUnless(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
654
                    os.O_NONBLOCK)
655

  
656
  def testDisable(self):
657
    utils.SetNonblockFlag(self.tmpfile.fileno(), False)
658
    self.failIf(fcntl.fcntl(self.tmpfile.fileno(), fcntl.F_GETFL) &
659
                os.O_NONBLOCK)
660

  
661

  
662 630
class TestRemoveFile(unittest.TestCase):
663 631
  """Test case for the RemoveFile function"""
664 632

  
......
1801 1769
    self.assertEqual(errcode, utils.CERT_ERROR)
1802 1770

  
1803 1771

  
1804
class TestIgnoreSignals(unittest.TestCase):
1805
  """Test the IgnoreSignals decorator"""
1806

  
1807
  @staticmethod
1808
  def _Raise(exception):
1809
    raise exception
1810

  
1811
  @staticmethod
1812
  def _Return(rval):
1813
    return rval
1814

  
1815
  def testIgnoreSignals(self):
1816
    sock_err_intr = socket.error(errno.EINTR, "Message")
1817
    sock_err_inval = socket.error(errno.EINVAL, "Message")
1818

  
1819
    env_err_intr = EnvironmentError(errno.EINTR, "Message")
1820
    env_err_inval = EnvironmentError(errno.EINVAL, "Message")
1821

  
1822
    self.assertRaises(socket.error, self._Raise, sock_err_intr)
1823
    self.assertRaises(socket.error, self._Raise, sock_err_inval)
1824
    self.assertRaises(EnvironmentError, self._Raise, env_err_intr)
1825
    self.assertRaises(EnvironmentError, self._Raise, env_err_inval)
1826

  
1827
    self.assertEquals(utils.IgnoreSignals(self._Raise, sock_err_intr), None)
1828
    self.assertEquals(utils.IgnoreSignals(self._Raise, env_err_intr), None)
1829
    self.assertRaises(socket.error, utils.IgnoreSignals, self._Raise,
1830
                      sock_err_inval)
1831
    self.assertRaises(EnvironmentError, utils.IgnoreSignals, self._Raise,
1832
                      env_err_inval)
1833

  
1834
    self.assertEquals(utils.IgnoreSignals(self._Return, True), True)
1835
    self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
1836

  
1837

  
1838 1772
class TestEnsureDirs(unittest.TestCase):
1839 1773
  """Tests for EnsureDirs"""
1840 1774

  
......
1857 1791
    os.umask(self.old_umask)
1858 1792

  
1859 1793

  
1860
class TestIgnoreProcessNotFound(unittest.TestCase):
1861
  @staticmethod
1862
  def _WritePid(fd):
1863
    os.write(fd, str(os.getpid()))
1864
    os.close(fd)
1865
    return True
1866

  
1867
  def test(self):
1868
    (pid_read_fd, pid_write_fd) = os.pipe()
1869

  
1870
    # Start short-lived process which writes its PID to pipe
1871
    self.assert_(utils.RunInSeparateProcess(self._WritePid, pid_write_fd))
1872
    os.close(pid_write_fd)
1873

  
1874
    # Read PID from pipe
1875
    pid = int(os.read(pid_read_fd, 1024))
1876
    os.close(pid_read_fd)
1877

  
1878
    # Try to send signal to process which exited recently
1879
    self.assertFalse(utils.IgnoreProcessNotFound(os.kill, pid, 0))
1880

  
1881

  
1882 1794
class TestFindMatch(unittest.TestCase):
1883 1795
  def test(self):
1884 1796
    data = {

Also available in: Unified diff