Revision 9d1b963f
b/Makefile.am | ||
---|---|---|
214 | 214 |
utils_PYTHON = \ |
215 | 215 |
lib/utils/__init__.py \ |
216 | 216 |
lib/utils/algo.py \ |
217 |
lib/utils/filelock.py \ |
|
217 | 218 |
lib/utils/hash.py \ |
218 | 219 |
lib/utils/log.py \ |
219 | 220 |
lib/utils/mlock.py \ |
... | ... | |
486 | 487 |
test/ganeti.ssh_unittest.py \ |
487 | 488 |
test/ganeti.uidpool_unittest.py \ |
488 | 489 |
test/ganeti.utils.algo_unittest.py \ |
490 |
test/ganeti.utils.filelock_unittest.py \ |
|
489 | 491 |
test/ganeti.utils.hash_unittest.py \ |
490 | 492 |
test/ganeti.utils.mlock_unittest.py \ |
491 | 493 |
test/ganeti.utils.retry_unittest.py \ |
b/lib/utils/__init__.py | ||
---|---|---|
60 | 60 |
from ganeti.utils.log import * # pylint: disable-msg=W0401 |
61 | 61 |
from ganeti.utils.hash import * # pylint: disable-msg=W0401 |
62 | 62 |
from ganeti.utils.wrapper import * # pylint: disable-msg=W0401 |
63 |
from ganeti.utils.filelock import * # pylint: disable-msg=W0401 |
|
63 | 64 |
|
64 | 65 |
|
65 | 66 |
#: when set to True, L{RunCmd} is disabled |
... | ... | |
2549 | 2550 |
return bool(exitcode) |
2550 | 2551 |
|
2551 | 2552 |
|
2552 |
def LockFile(fd): |
|
2553 |
"""Locks a file using POSIX locks. |
|
2554 |
|
|
2555 |
@type fd: int |
|
2556 |
@param fd: the file descriptor we need to lock |
|
2557 |
|
|
2558 |
""" |
|
2559 |
try: |
|
2560 |
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
2561 |
except IOError, err: |
|
2562 |
if err.errno == errno.EAGAIN: |
|
2563 |
raise errors.LockError("File already locked") |
|
2564 |
raise |
|
2565 |
|
|
2566 |
|
|
2567 | 2553 |
def ReadWatcherPauseFile(filename, now=None, remove_after=3600): |
2568 | 2554 |
"""Reads the watcher pause file. |
2569 | 2555 |
|
... | ... | |
2658 | 2644 |
WriteFile(filename, mode=0400, data=key_pem + cert_pem) |
2659 | 2645 |
|
2660 | 2646 |
|
2661 |
class FileLock(object): |
|
2662 |
"""Utility class for file locks. |
|
2663 |
|
|
2664 |
""" |
|
2665 |
def __init__(self, fd, filename): |
|
2666 |
"""Constructor for FileLock. |
|
2667 |
|
|
2668 |
@type fd: file |
|
2669 |
@param fd: File object |
|
2670 |
@type filename: str |
|
2671 |
@param filename: Path of the file opened at I{fd} |
|
2672 |
|
|
2673 |
""" |
|
2674 |
self.fd = fd |
|
2675 |
self.filename = filename |
|
2676 |
|
|
2677 |
@classmethod |
|
2678 |
def Open(cls, filename): |
|
2679 |
"""Creates and opens a file to be used as a file-based lock. |
|
2680 |
|
|
2681 |
@type filename: string |
|
2682 |
@param filename: path to the file to be locked |
|
2683 |
|
|
2684 |
""" |
|
2685 |
# Using "os.open" is necessary to allow both opening existing file |
|
2686 |
# read/write and creating if not existing. Vanilla "open" will truncate an |
|
2687 |
# existing file -or- allow creating if not existing. |
|
2688 |
return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"), |
|
2689 |
filename) |
|
2690 |
|
|
2691 |
def __del__(self): |
|
2692 |
self.Close() |
|
2693 |
|
|
2694 |
def Close(self): |
|
2695 |
"""Close the file and release the lock. |
|
2696 |
|
|
2697 |
""" |
|
2698 |
if hasattr(self, "fd") and self.fd: |
|
2699 |
self.fd.close() |
|
2700 |
self.fd = None |
|
2701 |
|
|
2702 |
def _flock(self, flag, blocking, timeout, errmsg): |
|
2703 |
"""Wrapper for fcntl.flock. |
|
2704 |
|
|
2705 |
@type flag: int |
|
2706 |
@param flag: operation flag |
|
2707 |
@type blocking: bool |
|
2708 |
@param blocking: whether the operation should be done in blocking mode. |
|
2709 |
@type timeout: None or float |
|
2710 |
@param timeout: for how long the operation should be retried (implies |
|
2711 |
non-blocking mode). |
|
2712 |
@type errmsg: string |
|
2713 |
@param errmsg: error message in case operation fails. |
|
2714 |
|
|
2715 |
""" |
|
2716 |
assert self.fd, "Lock was closed" |
|
2717 |
assert timeout is None or timeout >= 0, \ |
|
2718 |
"If specified, timeout must be positive" |
|
2719 |
assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set" |
|
2720 |
|
|
2721 |
# When a timeout is used, LOCK_NB must always be set |
|
2722 |
if not (timeout is None and blocking): |
|
2723 |
flag |= fcntl.LOCK_NB |
|
2724 |
|
|
2725 |
if timeout is None: |
|
2726 |
self._Lock(self.fd, flag, timeout) |
|
2727 |
else: |
|
2728 |
try: |
|
2729 |
Retry(self._Lock, (0.1, 1.2, 1.0), timeout, |
|
2730 |
args=(self.fd, flag, timeout)) |
|
2731 |
except RetryTimeout: |
|
2732 |
raise errors.LockError(errmsg) |
|
2733 |
|
|
2734 |
@staticmethod |
|
2735 |
def _Lock(fd, flag, timeout): |
|
2736 |
try: |
|
2737 |
fcntl.flock(fd, flag) |
|
2738 |
except IOError, err: |
|
2739 |
if timeout is not None and err.errno == errno.EAGAIN: |
|
2740 |
raise RetryAgain() |
|
2741 |
|
|
2742 |
logging.exception("fcntl.flock failed") |
|
2743 |
raise |
|
2744 |
|
|
2745 |
def Exclusive(self, blocking=False, timeout=None): |
|
2746 |
"""Locks the file in exclusive mode. |
|
2747 |
|
|
2748 |
@type blocking: boolean |
|
2749 |
@param blocking: whether to block and wait until we |
|
2750 |
can lock the file or return immediately |
|
2751 |
@type timeout: int or None |
|
2752 |
@param timeout: if not None, the duration to wait for the lock |
|
2753 |
(in blocking mode) |
|
2754 |
|
|
2755 |
""" |
|
2756 |
self._flock(fcntl.LOCK_EX, blocking, timeout, |
|
2757 |
"Failed to lock %s in exclusive mode" % self.filename) |
|
2758 |
|
|
2759 |
def Shared(self, blocking=False, timeout=None): |
|
2760 |
"""Locks the file in shared mode. |
|
2761 |
|
|
2762 |
@type blocking: boolean |
|
2763 |
@param blocking: whether to block and wait until we |
|
2764 |
can lock the file or return immediately |
|
2765 |
@type timeout: int or None |
|
2766 |
@param timeout: if not None, the duration to wait for the lock |
|
2767 |
(in blocking mode) |
|
2768 |
|
|
2769 |
""" |
|
2770 |
self._flock(fcntl.LOCK_SH, blocking, timeout, |
|
2771 |
"Failed to lock %s in shared mode" % self.filename) |
|
2772 |
|
|
2773 |
def Unlock(self, blocking=True, timeout=None): |
|
2774 |
"""Unlocks the file. |
|
2775 |
|
|
2776 |
According to C{flock(2)}, unlocking can also be a nonblocking |
|
2777 |
operation:: |
|
2778 |
|
|
2779 |
To make a non-blocking request, include LOCK_NB with any of the above |
|
2780 |
operations. |
|
2781 |
|
|
2782 |
@type blocking: boolean |
|
2783 |
@param blocking: whether to block and wait until we |
|
2784 |
can lock the file or return immediately |
|
2785 |
@type timeout: int or None |
|
2786 |
@param timeout: if not None, the duration to wait for the lock |
|
2787 |
(in blocking mode) |
|
2788 |
|
|
2789 |
""" |
|
2790 |
self._flock(fcntl.LOCK_UN, blocking, timeout, |
|
2791 |
"Failed to unlock %s" % self.filename) |
|
2792 |
|
|
2793 |
|
|
2794 | 2647 |
def SignalHandled(signums): |
2795 | 2648 |
"""Signal Handled decoration. |
2796 | 2649 |
|
b/lib/utils/filelock.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 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 |
|
32 |
|
|
33 |
|
|
34 |
def LockFile(fd): |
|
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 |
|
|
48 |
|
|
49 |
class FileLock(object): |
|
50 |
"""Utility class for file locks. |
|
51 |
|
|
52 |
""" |
|
53 |
def __init__(self, fd, filename): |
|
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 |
# Using "os.open" is necessary to allow both opening existing file |
|
74 |
# read/write and creating if not existing. Vanilla "open" will truncate an |
|
75 |
# existing file -or- allow creating if not existing. |
|
76 |
return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"), |
|
77 |
filename) |
|
78 |
|
|
79 |
def __del__(self): |
|
80 |
self.Close() |
|
81 |
|
|
82 |
def Close(self): |
|
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 |
# When a timeout is used, LOCK_NB must always be set |
|
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) |
b/test/ganeti.utils.filelock_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.filelock""" |
|
23 |
|
|
24 |
import os |
|
25 |
import tempfile |
|
26 |
import unittest |
|
27 |
|
|
28 |
from ganeti import constants |
|
29 |
from ganeti import utils |
|
30 |
from ganeti import errors |
|
31 |
|
|
32 |
import testutils |
|
33 |
|
|
34 |
|
|
35 |
class _BaseFileLockTest: |
|
36 |
"""Test case for the FileLock class""" |
|
37 |
|
|
38 |
def testSharedNonblocking(self): |
|
39 |
self.lock.Shared(blocking=False) |
|
40 |
self.lock.Close() |
|
41 |
|
|
42 |
def testExclusiveNonblocking(self): |
|
43 |
self.lock.Exclusive(blocking=False) |
|
44 |
self.lock.Close() |
|
45 |
|
|
46 |
def testUnlockNonblocking(self): |
|
47 |
self.lock.Unlock(blocking=False) |
|
48 |
self.lock.Close() |
|
49 |
|
|
50 |
def testSharedBlocking(self): |
|
51 |
self.lock.Shared(blocking=True) |
|
52 |
self.lock.Close() |
|
53 |
|
|
54 |
def testExclusiveBlocking(self): |
|
55 |
self.lock.Exclusive(blocking=True) |
|
56 |
self.lock.Close() |
|
57 |
|
|
58 |
def testUnlockBlocking(self): |
|
59 |
self.lock.Unlock(blocking=True) |
|
60 |
self.lock.Close() |
|
61 |
|
|
62 |
def testSharedExclusiveUnlock(self): |
|
63 |
self.lock.Shared(blocking=False) |
|
64 |
self.lock.Exclusive(blocking=False) |
|
65 |
self.lock.Unlock(blocking=False) |
|
66 |
self.lock.Close() |
|
67 |
|
|
68 |
def testExclusiveSharedUnlock(self): |
|
69 |
self.lock.Exclusive(blocking=False) |
|
70 |
self.lock.Shared(blocking=False) |
|
71 |
self.lock.Unlock(blocking=False) |
|
72 |
self.lock.Close() |
|
73 |
|
|
74 |
def testSimpleTimeout(self): |
|
75 |
# These will succeed on the first attempt, hence a short timeout |
|
76 |
self.lock.Shared(blocking=True, timeout=10.0) |
|
77 |
self.lock.Exclusive(blocking=False, timeout=10.0) |
|
78 |
self.lock.Unlock(blocking=True, timeout=10.0) |
|
79 |
self.lock.Close() |
|
80 |
|
|
81 |
@staticmethod |
|
82 |
def _TryLockInner(filename, shared, blocking): |
|
83 |
lock = utils.FileLock.Open(filename) |
|
84 |
|
|
85 |
if shared: |
|
86 |
fn = lock.Shared |
|
87 |
else: |
|
88 |
fn = lock.Exclusive |
|
89 |
|
|
90 |
try: |
|
91 |
# The timeout doesn't really matter as the parent process waits for us to |
|
92 |
# finish anyway. |
|
93 |
fn(blocking=blocking, timeout=0.01) |
|
94 |
except errors.LockError, err: |
|
95 |
return False |
|
96 |
|
|
97 |
return True |
|
98 |
|
|
99 |
def _TryLock(self, *args): |
|
100 |
return utils.RunInSeparateProcess(self._TryLockInner, self.tmpfile.name, |
|
101 |
*args) |
|
102 |
|
|
103 |
def testTimeout(self): |
|
104 |
for blocking in [True, False]: |
|
105 |
self.lock.Exclusive(blocking=True) |
|
106 |
self.failIf(self._TryLock(False, blocking)) |
|
107 |
self.failIf(self._TryLock(True, blocking)) |
|
108 |
|
|
109 |
self.lock.Shared(blocking=True) |
|
110 |
self.assert_(self._TryLock(True, blocking)) |
|
111 |
self.failIf(self._TryLock(False, blocking)) |
|
112 |
|
|
113 |
def testCloseShared(self): |
|
114 |
self.lock.Close() |
|
115 |
self.assertRaises(AssertionError, self.lock.Shared, blocking=False) |
|
116 |
|
|
117 |
def testCloseExclusive(self): |
|
118 |
self.lock.Close() |
|
119 |
self.assertRaises(AssertionError, self.lock.Exclusive, blocking=False) |
|
120 |
|
|
121 |
def testCloseUnlock(self): |
|
122 |
self.lock.Close() |
|
123 |
self.assertRaises(AssertionError, self.lock.Unlock, blocking=False) |
|
124 |
|
|
125 |
|
|
126 |
class TestFileLockWithFilename(testutils.GanetiTestCase, _BaseFileLockTest): |
|
127 |
TESTDATA = "Hello World\n" * 10 |
|
128 |
|
|
129 |
def setUp(self): |
|
130 |
testutils.GanetiTestCase.setUp(self) |
|
131 |
|
|
132 |
self.tmpfile = tempfile.NamedTemporaryFile() |
|
133 |
utils.WriteFile(self.tmpfile.name, data=self.TESTDATA) |
|
134 |
self.lock = utils.FileLock.Open(self.tmpfile.name) |
|
135 |
|
|
136 |
# Ensure "Open" didn't truncate file |
|
137 |
self.assertFileContent(self.tmpfile.name, self.TESTDATA) |
|
138 |
|
|
139 |
def tearDown(self): |
|
140 |
self.assertFileContent(self.tmpfile.name, self.TESTDATA) |
|
141 |
|
|
142 |
testutils.GanetiTestCase.tearDown(self) |
|
143 |
|
|
144 |
|
|
145 |
class TestFileLockWithFileObject(unittest.TestCase, _BaseFileLockTest): |
|
146 |
def setUp(self): |
|
147 |
self.tmpfile = tempfile.NamedTemporaryFile() |
|
148 |
self.lock = utils.FileLock(open(self.tmpfile.name, "w"), self.tmpfile.name) |
|
149 |
|
|
150 |
|
|
151 |
if __name__ == "__main__": |
|
152 |
testutils.GanetiTestProgram() |
b/test/ganeti.utils_unittest.py | ||
---|---|---|
1165 | 1165 |
self.failUnlessEqual(TailFile(fname, lines=i), data[-i:]) |
1166 | 1166 |
|
1167 | 1167 |
|
1168 |
class _BaseFileLockTest: |
|
1169 |
"""Test case for the FileLock class""" |
|
1170 |
|
|
1171 |
def testSharedNonblocking(self): |
|
1172 |
self.lock.Shared(blocking=False) |
|
1173 |
self.lock.Close() |
|
1174 |
|
|
1175 |
def testExclusiveNonblocking(self): |
|
1176 |
self.lock.Exclusive(blocking=False) |
|
1177 |
self.lock.Close() |
|
1178 |
|
|
1179 |
def testUnlockNonblocking(self): |
|
1180 |
self.lock.Unlock(blocking=False) |
|
1181 |
self.lock.Close() |
|
1182 |
|
|
1183 |
def testSharedBlocking(self): |
|
1184 |
self.lock.Shared(blocking=True) |
|
1185 |
self.lock.Close() |
|
1186 |
|
|
1187 |
def testExclusiveBlocking(self): |
|
1188 |
self.lock.Exclusive(blocking=True) |
|
1189 |
self.lock.Close() |
|
1190 |
|
|
1191 |
def testUnlockBlocking(self): |
|
1192 |
self.lock.Unlock(blocking=True) |
|
1193 |
self.lock.Close() |
|
1194 |
|
|
1195 |
def testSharedExclusiveUnlock(self): |
|
1196 |
self.lock.Shared(blocking=False) |
|
1197 |
self.lock.Exclusive(blocking=False) |
|
1198 |
self.lock.Unlock(blocking=False) |
|
1199 |
self.lock.Close() |
|
1200 |
|
|
1201 |
def testExclusiveSharedUnlock(self): |
|
1202 |
self.lock.Exclusive(blocking=False) |
|
1203 |
self.lock.Shared(blocking=False) |
|
1204 |
self.lock.Unlock(blocking=False) |
|
1205 |
self.lock.Close() |
|
1206 |
|
|
1207 |
def testSimpleTimeout(self): |
|
1208 |
# These will succeed on the first attempt, hence a short timeout |
|
1209 |
self.lock.Shared(blocking=True, timeout=10.0) |
|
1210 |
self.lock.Exclusive(blocking=False, timeout=10.0) |
|
1211 |
self.lock.Unlock(blocking=True, timeout=10.0) |
|
1212 |
self.lock.Close() |
|
1213 |
|
|
1214 |
@staticmethod |
|
1215 |
def _TryLockInner(filename, shared, blocking): |
|
1216 |
lock = utils.FileLock.Open(filename) |
|
1217 |
|
|
1218 |
if shared: |
|
1219 |
fn = lock.Shared |
|
1220 |
else: |
|
1221 |
fn = lock.Exclusive |
|
1222 |
|
|
1223 |
try: |
|
1224 |
# The timeout doesn't really matter as the parent process waits for us to |
|
1225 |
# finish anyway. |
|
1226 |
fn(blocking=blocking, timeout=0.01) |
|
1227 |
except errors.LockError, err: |
|
1228 |
return False |
|
1229 |
|
|
1230 |
return True |
|
1231 |
|
|
1232 |
def _TryLock(self, *args): |
|
1233 |
return utils.RunInSeparateProcess(self._TryLockInner, self.tmpfile.name, |
|
1234 |
*args) |
|
1235 |
|
|
1236 |
def testTimeout(self): |
|
1237 |
for blocking in [True, False]: |
|
1238 |
self.lock.Exclusive(blocking=True) |
|
1239 |
self.failIf(self._TryLock(False, blocking)) |
|
1240 |
self.failIf(self._TryLock(True, blocking)) |
|
1241 |
|
|
1242 |
self.lock.Shared(blocking=True) |
|
1243 |
self.assert_(self._TryLock(True, blocking)) |
|
1244 |
self.failIf(self._TryLock(False, blocking)) |
|
1245 |
|
|
1246 |
def testCloseShared(self): |
|
1247 |
self.lock.Close() |
|
1248 |
self.assertRaises(AssertionError, self.lock.Shared, blocking=False) |
|
1249 |
|
|
1250 |
def testCloseExclusive(self): |
|
1251 |
self.lock.Close() |
|
1252 |
self.assertRaises(AssertionError, self.lock.Exclusive, blocking=False) |
|
1253 |
|
|
1254 |
def testCloseUnlock(self): |
|
1255 |
self.lock.Close() |
|
1256 |
self.assertRaises(AssertionError, self.lock.Unlock, blocking=False) |
|
1257 |
|
|
1258 |
|
|
1259 |
class TestFileLockWithFilename(testutils.GanetiTestCase, _BaseFileLockTest): |
|
1260 |
TESTDATA = "Hello World\n" * 10 |
|
1261 |
|
|
1262 |
def setUp(self): |
|
1263 |
testutils.GanetiTestCase.setUp(self) |
|
1264 |
|
|
1265 |
self.tmpfile = tempfile.NamedTemporaryFile() |
|
1266 |
utils.WriteFile(self.tmpfile.name, data=self.TESTDATA) |
|
1267 |
self.lock = utils.FileLock.Open(self.tmpfile.name) |
|
1268 |
|
|
1269 |
# Ensure "Open" didn't truncate file |
|
1270 |
self.assertFileContent(self.tmpfile.name, self.TESTDATA) |
|
1271 |
|
|
1272 |
def tearDown(self): |
|
1273 |
self.assertFileContent(self.tmpfile.name, self.TESTDATA) |
|
1274 |
|
|
1275 |
testutils.GanetiTestCase.tearDown(self) |
|
1276 |
|
|
1277 |
|
|
1278 |
class TestFileLockWithFileObject(unittest.TestCase, _BaseFileLockTest): |
|
1279 |
def setUp(self): |
|
1280 |
self.tmpfile = tempfile.NamedTemporaryFile() |
|
1281 |
self.lock = utils.FileLock(open(self.tmpfile.name, "w"), self.tmpfile.name) |
|
1282 |
|
|
1283 |
|
|
1284 | 1168 |
class TestTimeFunctions(unittest.TestCase): |
1285 | 1169 |
"""Test case for time functions""" |
1286 | 1170 |
|
Also available in: Unified diff