(2.10) Some fixes in _GenerateKVMBlockDevicesOptions()
[ganeti-local] / lib / utils / wrapper.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2010, 2011, 2012 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 sys
26 import time
27 import socket
28 import errno
29 import tempfile
30 import fcntl
31 import os
32 import select
33 import logging
34
35
36 def TestDelay(duration):
37   """Sleep for a fixed amount of time.
38
39   @type duration: float
40   @param duration: the sleep duration, in seconds
41   @rtype: (boolean, str)
42   @return: False for negative value, and an accompanying error message;
43       True otherwise (and msg is None)
44
45   """
46   if duration < 0:
47     return False, "Invalid sleep duration"
48   time.sleep(duration)
49   return True, None
50
51
52 def CloseFdNoError(fd, retries=5):
53   """Close a file descriptor ignoring errors.
54
55   @type fd: int
56   @param fd: the file descriptor
57   @type retries: int
58   @param retries: how many retries to make, in case we get any
59       other error than EBADF
60
61   """
62   try:
63     os.close(fd)
64   except OSError, err:
65     if err.errno != errno.EBADF:
66       if retries > 0:
67         CloseFdNoError(fd, retries - 1)
68     # else either it's closed already or we're out of retries, so we
69     # ignore this and go on
70
71
72 def SetCloseOnExecFlag(fd, enable):
73   """Sets or unsets the close-on-exec flag on a file descriptor.
74
75   @type fd: int
76   @param fd: File descriptor
77   @type enable: bool
78   @param enable: Whether to set or unset it.
79
80   """
81   flags = fcntl.fcntl(fd, fcntl.F_GETFD)
82
83   if enable:
84     flags |= fcntl.FD_CLOEXEC
85   else:
86     flags &= ~fcntl.FD_CLOEXEC
87
88   fcntl.fcntl(fd, fcntl.F_SETFD, flags)
89
90
91 def SetNonblockFlag(fd, enable):
92   """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
93
94   @type fd: int
95   @param fd: File descriptor
96   @type enable: bool
97   @param enable: Whether to set or unset it
98
99   """
100   flags = fcntl.fcntl(fd, fcntl.F_GETFL)
101
102   if enable:
103     flags |= os.O_NONBLOCK
104   else:
105     flags &= ~os.O_NONBLOCK
106
107   fcntl.fcntl(fd, fcntl.F_SETFL, flags)
108
109
110 def RetryOnSignal(fn, *args, **kwargs):
111   """Calls a function again if it failed due to EINTR.
112
113   """
114   while True:
115     try:
116       return fn(*args, **kwargs)
117     except EnvironmentError, err:
118       if err.errno != errno.EINTR:
119         raise
120     except (socket.error, select.error), err:
121       # In python 2.6 and above select.error is an IOError, so it's handled
122       # above, in 2.5 and below it's not, and it's handled here.
123       if not (err.args and err.args[0] == errno.EINTR):
124         raise
125
126
127 def IgnoreProcessNotFound(fn, *args, **kwargs):
128   """Ignores ESRCH when calling a process-related function.
129
130   ESRCH is raised when a process is not found.
131
132   @rtype: bool
133   @return: Whether process was found
134
135   """
136   try:
137     fn(*args, **kwargs)
138   except EnvironmentError, err:
139     # Ignore ESRCH
140     if err.errno == errno.ESRCH:
141       return False
142     raise
143
144   return True
145
146
147 def IgnoreSignals(fn, *args, **kwargs):
148   """Tries to call a function ignoring failures due to EINTR.
149
150   """
151   try:
152     return fn(*args, **kwargs)
153   except EnvironmentError, err:
154     if err.errno == errno.EINTR:
155       return None
156     else:
157       raise
158   except (select.error, socket.error), err:
159     # In python 2.6 and above select.error is an IOError, so it's handled
160     # above, in 2.5 and below it's not, and it's handled here.
161     if err.args and err.args[0] == errno.EINTR:
162       return None
163     else:
164       raise
165
166
167 def GetClosedTempfile(*args, **kwargs):
168   """Creates a temporary file and returns its path.
169
170   """
171   (fd, path) = tempfile.mkstemp(*args, **kwargs)
172   CloseFdNoError(fd)
173   return path
174
175
176 def IsExecutable(filename):
177   """Checks whether a file exists and is executable.
178
179   @type filename: string
180   @param filename: Filename
181   @rtype: bool
182
183   """
184   return os.path.isfile(filename) and os.access(filename, os.X_OK)
185
186
187 def ResetTempfileModule(_time=time.time):
188   """Resets the random name generator of the tempfile module.
189
190   This function should be called after C{os.fork} in the child process to
191   ensure it creates a newly seeded random generator. Otherwise it would
192   generate the same random parts as the parent process. If several processes
193   race for the creation of a temporary file, this could lead to one not getting
194   a temporary name.
195
196   """
197   # pylint: disable=W0212
198   if ((sys.hexversion >= 0x020703F0 and sys.hexversion < 0x03000000) or
199       sys.hexversion >= 0x030203F0):
200     # Python 2.7 automatically resets the RNG on pid changes (i.e. forking)
201     return
202
203   try:
204     lock = tempfile._once_lock
205     lock.acquire()
206     try:
207       # Re-seed random name generator
208       if tempfile._name_sequence:
209         tempfile._name_sequence.rng.seed(hash(_time()) ^ os.getpid())
210     finally:
211       lock.release()
212   except AttributeError:
213     logging.critical("The tempfile module misses at least one of the"
214                      " '_once_lock' and '_name_sequence' attributes")