1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """Utility functions for file-based locks.
31
32 """
33
34 import fcntl
35 import errno
36 import os
37 import logging
38
39 from ganeti import errors
40 from ganeti.utils import retry
44 """Locks a file using POSIX locks.
45
46 @type fd: int
47 @param fd: the file descriptor we need to lock
48
49 """
50 try:
51 fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
52 except IOError, err:
53 if err.errno == errno.EAGAIN:
54 raise errors.LockError("File already locked")
55 raise
56
59 """Utility class for file locks.
60
61 """
63 """Constructor for FileLock.
64
65 @type fd: file
66 @param fd: File object
67 @type filename: str
68 @param filename: Path of the file opened at I{fd}
69
70 """
71 self.fd = fd
72 self.filename = filename
73
74 @classmethod
75 - def Open(cls, filename):
76 """Creates and opens a file to be used as a file-based lock.
77
78 @type filename: string
79 @param filename: path to the file to be locked
80
81 """
82
83
84
85 return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT, 0664), "w+"),
86 filename)
87
90
92 """Close the file and release the lock.
93
94 """
95 if hasattr(self, "fd") and self.fd:
96 self.fd.close()
97 self.fd = None
98
99 - def _flock(self, flag, blocking, timeout, errmsg):
100 """Wrapper for fcntl.flock.
101
102 @type flag: int
103 @param flag: operation flag
104 @type blocking: bool
105 @param blocking: whether the operation should be done in blocking mode.
106 @type timeout: None or float
107 @param timeout: for how long the operation should be retried (implies
108 non-blocking mode).
109 @type errmsg: string
110 @param errmsg: error message in case operation fails.
111
112 """
113 assert self.fd, "Lock was closed"
114 assert timeout is None or timeout >= 0, \
115 "If specified, timeout must be positive"
116 assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
117
118
119 if not (timeout is None and blocking):
120 flag |= fcntl.LOCK_NB
121
122 if timeout is None:
123 self._Lock(self.fd, flag, timeout)
124 else:
125 try:
126 retry.Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
127 args=(self.fd, flag, timeout))
128 except retry.RetryTimeout:
129 raise errors.LockError(errmsg)
130
131 @staticmethod
132 - def _Lock(fd, flag, timeout):
133 try:
134 fcntl.flock(fd, flag)
135 except IOError, err:
136 if timeout is not None and err.errno == errno.EAGAIN:
137 raise retry.RetryAgain()
138
139 logging.exception("fcntl.flock failed")
140 raise
141
142 - def Exclusive(self, blocking=False, timeout=None):
143 """Locks the file in exclusive mode.
144
145 @type blocking: boolean
146 @param blocking: whether to block and wait until we
147 can lock the file or return immediately
148 @type timeout: int or None
149 @param timeout: if not None, the duration to wait for the lock
150 (in blocking mode)
151
152 """
153 self._flock(fcntl.LOCK_EX, blocking, timeout,
154 "Failed to lock %s in exclusive mode" % self.filename)
155
156 - def Shared(self, blocking=False, timeout=None):
157 """Locks the file in shared mode.
158
159 @type blocking: boolean
160 @param blocking: whether to block and wait until we
161 can lock the file or return immediately
162 @type timeout: int or None
163 @param timeout: if not None, the duration to wait for the lock
164 (in blocking mode)
165
166 """
167 self._flock(fcntl.LOCK_SH, blocking, timeout,
168 "Failed to lock %s in shared mode" % self.filename)
169
170 - def Unlock(self, blocking=True, timeout=None):
171 """Unlocks the file.
172
173 According to C{flock(2)}, unlocking can also be a nonblocking
174 operation::
175
176 To make a non-blocking request, include LOCK_NB with any of the above
177 operations.
178
179 @type blocking: boolean
180 @param blocking: whether to block and wait until we
181 can lock the file or return immediately
182 @type timeout: int or None
183 @param timeout: if not None, the duration to wait for the lock
184 (in blocking mode)
185
186 """
187 self._flock(fcntl.LOCK_UN, blocking, timeout,
188 "Failed to unlock %s" % self.filename)
189