Revision e4e35357
b/lib/locking.py | ||
---|---|---|
33 | 33 |
import logging |
34 | 34 |
import heapq |
35 | 35 |
import operator |
36 |
import itertools |
|
36 | 37 |
|
37 | 38 |
from ganeti import errors |
38 | 39 |
from ganeti import utils |
... | ... | |
1514 | 1515 |
return self.__keyring[level].remove(names) |
1515 | 1516 |
|
1516 | 1517 |
|
1518 |
def _MonitorSortKey((num, item)): |
|
1519 |
"""Sorting key function. |
|
1520 |
|
|
1521 |
Sort by name, then by incoming order. |
|
1522 |
|
|
1523 |
""" |
|
1524 |
(name, _, _, _) = item |
|
1525 |
|
|
1526 |
return (utils.NiceSortKey(name), num) |
|
1527 |
|
|
1528 |
|
|
1517 | 1529 |
class LockMonitor(object): |
1518 | 1530 |
_LOCK_ATTR = "_lock" |
1519 | 1531 |
|
... | ... | |
1523 | 1535 |
""" |
1524 | 1536 |
self._lock = SharedLock("LockMonitor") |
1525 | 1537 |
|
1538 |
# Counter for stable sorting |
|
1539 |
self._counter = itertools.count(0) |
|
1540 |
|
|
1526 | 1541 |
# Tracked locks. Weak references are used to avoid issues with circular |
1527 | 1542 |
# references and deletion. |
1528 | 1543 |
self._locks = weakref.WeakKeyDictionary() |
... | ... | |
1534 | 1549 |
""" |
1535 | 1550 |
logging.debug("Registering lock %s", lock.name) |
1536 | 1551 |
assert lock not in self._locks, "Duplicate lock registration" |
1537 |
assert not compat.any(lock.name == i.name for i in self._locks.keys()), \ |
|
1538 |
"Found duplicate lock name" |
|
1539 |
self._locks[lock] = None |
|
1552 |
|
|
1553 |
# There used to be a check for duplicate names here. As it turned out, when |
|
1554 |
# a lock is re-created with the same name in a very short timeframe, the |
|
1555 |
# previous instance might not yet be removed from the weakref dictionary. |
|
1556 |
# By keeping track of the order of incoming registrations, a stable sort |
|
1557 |
# ordering can still be guaranteed. |
|
1558 |
|
|
1559 |
self._locks[lock] = self._counter.next() |
|
1540 | 1560 |
|
1541 | 1561 |
@ssynchronized(_LOCK_ATTR) |
1542 | 1562 |
def _GetLockInfo(self, requested): |
1543 | 1563 |
"""Get information from all locks while the monitor lock is held. |
1544 | 1564 |
|
1545 | 1565 |
""" |
1546 |
return [lock.GetInfo(requested) for lock in self._locks.keys()]
|
|
1566 |
return [(num, lock.GetInfo(requested)) for lock, num in self._locks.items()]
|
|
1547 | 1567 |
|
1548 | 1568 |
def _Query(self, fields): |
1549 | 1569 |
"""Queries information from all locks. |
... | ... | |
1554 | 1574 |
""" |
1555 | 1575 |
qobj = query.Query(query.LOCK_FIELDS, fields) |
1556 | 1576 |
|
1557 |
# Get all data and sort by name |
|
1558 |
lockinfo = utils.NiceSort(self._GetLockInfo(qobj.RequestedData()), |
|
1559 |
key=operator.itemgetter(0)) |
|
1577 |
# Get all data with internal lock held and then sort by name and incoming |
|
1578 |
# order |
|
1579 |
lockinfo = sorted(self._GetLockInfo(qobj.RequestedData()), |
|
1580 |
key=_MonitorSortKey) |
|
1560 | 1581 |
|
1561 |
return (qobj, query.LockQueryData(lockinfo)) |
|
1582 |
# Extract lock information and build query data |
|
1583 |
return (qobj, query.LockQueryData(map(operator.itemgetter(1), lockinfo))) |
|
1562 | 1584 |
|
1563 | 1585 |
def QueryLocks(self, fields): |
1564 | 1586 |
"""Queries information from all locks. |
b/test/ganeti.locking_unittest.py | ||
---|---|---|
28 | 28 |
import Queue |
29 | 29 |
import threading |
30 | 30 |
import random |
31 |
import gc |
|
31 | 32 |
import itertools |
32 | 33 |
|
33 | 34 |
from ganeti import constants |
... | ... | |
1914 | 1915 |
|
1915 | 1916 |
self.assertEqual(len(self.lm._locks), 1) |
1916 | 1917 |
|
1918 |
def testDeleteAndRecreate(self): |
|
1919 |
lname = "TestLock101923193" |
|
1920 |
|
|
1921 |
# Create some locks with the same name and keep all references |
|
1922 |
locks = [locking.SharedLock(lname, monitor=self.lm) |
|
1923 |
for _ in range(5)] |
|
1924 |
|
|
1925 |
self.assertEqual(len(self.lm._locks), len(locks)) |
|
1926 |
|
|
1927 |
result = self.lm.QueryLocks(["name", "mode", "owner"]) |
|
1928 |
self.assertEqual(objects.QueryResponse.FromDict(result).data, |
|
1929 |
[[(constants.RS_NORMAL, lname), |
|
1930 |
(constants.RS_NORMAL, None), |
|
1931 |
(constants.RS_NORMAL, None)]] * 5) |
|
1932 |
|
|
1933 |
locks[2].delete() |
|
1934 |
|
|
1935 |
# Check information order |
|
1936 |
result = self.lm.QueryLocks(["name", "mode", "owner"]) |
|
1937 |
self.assertEqual(objects.QueryResponse.FromDict(result).data, |
|
1938 |
[[(constants.RS_NORMAL, lname), |
|
1939 |
(constants.RS_NORMAL, None), |
|
1940 |
(constants.RS_NORMAL, None)]] * 2 + |
|
1941 |
[[(constants.RS_NORMAL, lname), |
|
1942 |
(constants.RS_NORMAL, "deleted"), |
|
1943 |
(constants.RS_NORMAL, None)]] + |
|
1944 |
[[(constants.RS_NORMAL, lname), |
|
1945 |
(constants.RS_NORMAL, None), |
|
1946 |
(constants.RS_NORMAL, None)]] * 2) |
|
1947 |
|
|
1948 |
locks[1].acquire(shared=0) |
|
1949 |
|
|
1950 |
last_status = [ |
|
1951 |
[(constants.RS_NORMAL, lname), |
|
1952 |
(constants.RS_NORMAL, None), |
|
1953 |
(constants.RS_NORMAL, None)], |
|
1954 |
[(constants.RS_NORMAL, lname), |
|
1955 |
(constants.RS_NORMAL, "exclusive"), |
|
1956 |
(constants.RS_NORMAL, [threading.currentThread().getName()])], |
|
1957 |
[(constants.RS_NORMAL, lname), |
|
1958 |
(constants.RS_NORMAL, "deleted"), |
|
1959 |
(constants.RS_NORMAL, None)], |
|
1960 |
[(constants.RS_NORMAL, lname), |
|
1961 |
(constants.RS_NORMAL, None), |
|
1962 |
(constants.RS_NORMAL, None)], |
|
1963 |
[(constants.RS_NORMAL, lname), |
|
1964 |
(constants.RS_NORMAL, None), |
|
1965 |
(constants.RS_NORMAL, None)], |
|
1966 |
] |
|
1967 |
|
|
1968 |
# Check information order |
|
1969 |
result = self.lm.QueryLocks(["name", "mode", "owner"]) |
|
1970 |
self.assertEqual(objects.QueryResponse.FromDict(result).data, last_status) |
|
1971 |
|
|
1972 |
self.assertEqual(len(set(self.lm._locks.values())), len(locks)) |
|
1973 |
self.assertEqual(len(self.lm._locks), len(locks)) |
|
1974 |
|
|
1975 |
# Check lock deletion |
|
1976 |
for idx in range(len(locks)): |
|
1977 |
del locks[0] |
|
1978 |
assert gc.isenabled() |
|
1979 |
gc.collect() |
|
1980 |
self.assertEqual(len(self.lm._locks), len(locks)) |
|
1981 |
result = self.lm.QueryLocks(["name", "mode", "owner"]) |
|
1982 |
self.assertEqual(objects.QueryResponse.FromDict(result).data, |
|
1983 |
last_status[idx + 1:]) |
|
1984 |
|
|
1985 |
# All locks should have been deleted |
|
1986 |
assert not locks |
|
1987 |
self.assertFalse(self.lm._locks) |
|
1988 |
|
|
1989 |
result = self.lm.QueryLocks(["name", "mode", "owner"]) |
|
1990 |
self.assertEqual(objects.QueryResponse.FromDict(result).data, []) |
|
1991 |
|
|
1917 | 1992 |
|
1918 | 1993 |
if __name__ == '__main__': |
1919 | 1994 |
testutils.GanetiTestProgram() |
Also available in: Unified diff