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