Revision d4fa5c23
b/daemons/ganeti-masterd | ||
---|---|---|
393 | 393 |
|
394 | 394 |
logger.SetupDaemon(constants.LOG_MASTERDAEMON, debug=options.debug) |
395 | 395 |
|
396 |
logger.Info("ganeti master daemon startup")
|
|
396 |
logging.info("ganeti master daemon startup")
|
|
397 | 397 |
|
398 |
master.setup_queue() |
|
398 | 399 |
try: |
399 |
utils.Lock('cmd', debug=options.debug) |
|
400 |
except errors.LockError, err: |
|
401 |
print >> sys.stderr, str(err) |
|
402 |
master.server_cleanup() |
|
403 |
return |
|
404 |
|
|
405 |
try: |
|
406 |
master.setup_queue() |
|
407 |
try: |
|
408 |
master.serve_forever() |
|
409 |
finally: |
|
410 |
master.server_cleanup() |
|
400 |
master.serve_forever() |
|
411 | 401 |
finally: |
412 |
utils.Unlock('cmd') |
|
413 |
utils.LockCleanup() |
|
402 |
master.server_cleanup() |
|
414 | 403 |
|
415 | 404 |
|
416 | 405 |
if __name__ == "__main__": |
b/lib/cmdlib.py | ||
---|---|---|
1093 | 1093 |
if done or oneshot: |
1094 | 1094 |
break |
1095 | 1095 |
|
1096 |
if unlock: |
|
1097 |
#utils.Unlock('cmd') |
|
1098 |
pass |
|
1099 |
try: |
|
1100 |
time.sleep(min(60, max_time)) |
|
1101 |
finally: |
|
1102 |
if unlock: |
|
1103 |
#utils.Lock('cmd') |
|
1104 |
pass |
|
1096 |
time.sleep(min(60, max_time)) |
|
1105 | 1097 |
|
1106 | 1098 |
if done: |
1107 | 1099 |
proc.LogInfo("Instance %s's disks are in sync." % instance.name) |
b/lib/utils.py | ||
---|---|---|
101 | 101 |
output = property(_GetOutput, None, None, "Return full output") |
102 | 102 |
|
103 | 103 |
|
104 |
def _GetLockFile(subsystem): |
|
105 |
"""Compute the file name for a given lock name.""" |
|
106 |
return "%s/ganeti_lock_%s" % (constants.LOCK_DIR, subsystem) |
|
107 |
|
|
108 |
|
|
109 |
def Lock(name, max_retries=None, debug=False): |
|
110 |
"""Lock a given subsystem. |
|
111 |
|
|
112 |
In case the lock is already held by an alive process, the function |
|
113 |
will sleep indefintely and poll with a one second interval. |
|
114 |
|
|
115 |
When the optional integer argument 'max_retries' is passed with a |
|
116 |
non-zero value, the function will sleep only for this number of |
|
117 |
times, and then it will will raise a LockError if the lock can't be |
|
118 |
acquired. Passing in a negative number will cause only one try to |
|
119 |
get the lock. Passing a positive number will make the function retry |
|
120 |
for approximately that number of seconds. |
|
121 |
|
|
122 |
""" |
|
123 |
lockfile = _GetLockFile(name) |
|
124 |
|
|
125 |
if name in _locksheld: |
|
126 |
raise errors.LockError('Lock "%s" already held!' % (name,)) |
|
127 |
|
|
128 |
errcount = 0 |
|
129 |
|
|
130 |
retries = 0 |
|
131 |
while True: |
|
132 |
try: |
|
133 |
fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_SYNC) |
|
134 |
break |
|
135 |
except OSError, creat_err: |
|
136 |
if creat_err.errno != errno.EEXIST: |
|
137 |
raise errors.LockError("Can't create the lock file. Error '%s'." % |
|
138 |
str(creat_err)) |
|
139 |
|
|
140 |
try: |
|
141 |
pf = open(lockfile, 'r') |
|
142 |
except IOError, open_err: |
|
143 |
errcount += 1 |
|
144 |
if errcount >= 5: |
|
145 |
raise errors.LockError("Lock file exists but cannot be opened." |
|
146 |
" Error: '%s'." % str(open_err)) |
|
147 |
time.sleep(1) |
|
148 |
continue |
|
149 |
|
|
150 |
try: |
|
151 |
pid = int(pf.read()) |
|
152 |
except ValueError: |
|
153 |
raise errors.LockError("Invalid pid string in %s" % |
|
154 |
(lockfile,)) |
|
155 |
|
|
156 |
if not IsProcessAlive(pid): |
|
157 |
raise errors.LockError("Stale lockfile %s for pid %d?" % |
|
158 |
(lockfile, pid)) |
|
159 |
|
|
160 |
if max_retries and max_retries <= retries: |
|
161 |
raise errors.LockError("Can't acquire lock during the specified" |
|
162 |
" time, aborting.") |
|
163 |
if retries == 5 and (debug or sys.stdin.isatty()): |
|
164 |
logger.ToStderr("Waiting for '%s' lock from pid %d..." % (name, pid)) |
|
165 |
|
|
166 |
time.sleep(1) |
|
167 |
retries += 1 |
|
168 |
continue |
|
169 |
|
|
170 |
os.write(fd, '%d\n' % (os.getpid(),)) |
|
171 |
os.close(fd) |
|
172 |
|
|
173 |
_locksheld.append(name) |
|
174 |
|
|
175 |
|
|
176 |
def Unlock(name): |
|
177 |
"""Unlock a given subsystem. |
|
178 |
|
|
179 |
""" |
|
180 |
lockfile = _GetLockFile(name) |
|
181 |
|
|
182 |
try: |
|
183 |
fd = os.open(lockfile, os.O_RDONLY) |
|
184 |
except OSError: |
|
185 |
raise errors.LockError('Lock "%s" not held.' % (name,)) |
|
186 |
|
|
187 |
f = os.fdopen(fd, 'r') |
|
188 |
pid_str = f.read() |
|
189 |
|
|
190 |
try: |
|
191 |
pid = int(pid_str) |
|
192 |
except ValueError: |
|
193 |
raise errors.LockError('Unable to determine PID of locking process.') |
|
194 |
|
|
195 |
if pid != os.getpid(): |
|
196 |
raise errors.LockError('Lock not held by me (%d != %d)' % |
|
197 |
(os.getpid(), pid,)) |
|
198 |
|
|
199 |
os.unlink(lockfile) |
|
200 |
_locksheld.remove(name) |
|
201 |
|
|
202 |
|
|
203 |
def LockCleanup(): |
|
204 |
"""Remove all locks. |
|
205 |
|
|
206 |
""" |
|
207 |
for lock in _locksheld: |
|
208 |
Unlock(lock) |
|
209 |
|
|
210 |
|
|
211 | 104 |
def RunCmd(cmd): |
212 | 105 |
"""Execute a (shell) command. |
213 | 106 |
|
... | ... | |
281 | 174 |
return RunResult(exitcode, signal, out, err, strcmd) |
282 | 175 |
|
283 | 176 |
|
284 |
def RunCmdUnlocked(cmd): |
|
285 |
"""Execute a shell command without the 'cmd' lock. |
|
286 |
|
|
287 |
This variant of `RunCmd()` drops the 'cmd' lock before running the |
|
288 |
command and re-aquires it afterwards, thus it can be used to call |
|
289 |
other ganeti commands. |
|
290 |
|
|
291 |
The argument and return values are the same as for the `RunCmd()` |
|
292 |
function. |
|
293 |
|
|
294 |
Args: |
|
295 |
cmd - command to run. (str) |
|
296 |
|
|
297 |
Returns: |
|
298 |
`RunResult` |
|
299 |
|
|
300 |
""" |
|
301 |
Unlock('cmd') |
|
302 |
ret = RunCmd(cmd) |
|
303 |
Lock('cmd') |
|
304 |
|
|
305 |
return ret |
|
306 |
|
|
307 |
|
|
308 | 177 |
def RemoveFile(filename): |
309 | 178 |
"""Remove a file ignoring some errors. |
310 | 179 |
|
b/test/ganeti.utils_unittest.py | ||
---|---|---|
99 | 99 |
"noexisting process detected") |
100 | 100 |
|
101 | 101 |
|
102 |
class TestLocking(unittest.TestCase): |
|
103 |
"""Testing case for the Lock/Unlock functions""" |
|
104 |
|
|
105 |
def setUp(self): |
|
106 |
lock_dir = tempfile.mkdtemp(prefix="ganeti.unittest.", |
|
107 |
suffix=".locking") |
|
108 |
self.old_lock_dir = constants.LOCK_DIR |
|
109 |
constants.LOCK_DIR = lock_dir |
|
110 |
|
|
111 |
def tearDown(self): |
|
112 |
try: |
|
113 |
ganeti.utils.Unlock("unittest") |
|
114 |
except LockError: |
|
115 |
pass |
|
116 |
shutil.rmtree(constants.LOCK_DIR, ignore_errors=True) |
|
117 |
constants.LOCK_DIR = self.old_lock_dir |
|
118 |
|
|
119 |
def clean_lock(self, name): |
|
120 |
try: |
|
121 |
ganeti.utils.Unlock("unittest") |
|
122 |
except LockError: |
|
123 |
pass |
|
124 |
|
|
125 |
|
|
126 |
def testLock(self): |
|
127 |
self.clean_lock("unittest") |
|
128 |
self.assertEqual(None, Lock("unittest")) |
|
129 |
|
|
130 |
|
|
131 |
def testUnlock(self): |
|
132 |
self.clean_lock("unittest") |
|
133 |
ganeti.utils.Lock("unittest") |
|
134 |
self.assertEqual(None, Unlock("unittest")) |
|
135 |
|
|
136 |
def testDoubleLock(self): |
|
137 |
self.clean_lock("unittest") |
|
138 |
ganeti.utils.Lock("unittest") |
|
139 |
self.assertRaises(LockError, Lock, "unittest") |
|
140 |
|
|
141 |
|
|
142 | 102 |
class TestRunCmd(unittest.TestCase): |
143 | 103 |
"""Testing case for the RunCmd function""" |
144 | 104 |
|
Also available in: Unified diff