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