Revision 24d16f76

b/lib/client/gnt_debug.py
479 479
  """
480 480
  selected_fields = ParseFields(opts.output, _LIST_LOCKS_DEF_FIELDS)
481 481

  
482
  if not opts.no_headers:
483
    headers = {
484
      "name": "Name",
485
      "mode": "Mode",
486
      "owner": "Owner",
487
      "pending": "Pending",
488
      }
489
  else:
490
    headers = None
482
  def _DashIfNone(fn):
483
    def wrapper(value):
484
      if not value:
485
        return "-"
486
      return fn(value)
487
    return wrapper
488

  
489
  def _FormatPending(value):
490
    """Format pending acquires.
491

  
492
    """
493
    return utils.CommaJoin("%s:%s" % (mode, ",".join(threads))
494
                           for mode, threads in value)
495

  
496
  # Format raw values
497
  fmtoverride = {
498
    "mode": (_DashIfNone(str), False),
499
    "owner": (_DashIfNone(",".join), False),
500
    "pending": (_DashIfNone(_FormatPending), False),
501
    }
491 502

  
492 503
  while True:
493
    # Not reusing client as interval might be too long
494
    output = GetClient().QueryLocks(selected_fields, False)
495

  
496
    # change raw values to nicer strings
497
    for row in output:
498
      for idx, field in enumerate(selected_fields):
499
        val = row[idx]
500

  
501
        if field in ("mode", "owner", "pending") and not val:
502
          val = "-"
503
        elif field == "owner":
504
          val = ",".join(val)
505
        elif field == "pending":
506
          val = utils.CommaJoin("%s:%s" % (mode, ",".join(threads))
507
                                for mode, threads in val)
508

  
509
        row[idx] = str(val)
510

  
511
    data = GenerateTable(separator=opts.separator, headers=headers,
512
                         fields=selected_fields, data=output)
513
    for line in data:
514
      ToStdout(line)
504
    ret = GenericList(constants.QR_LOCK, selected_fields, None, None,
505
                      opts.separator, not opts.no_headers,
506
                      format_override=fmtoverride)
507

  
508
    if ret != constants.EXIT_SUCCESS:
509
      return ret
515 510

  
516 511
    if not opts.interval:
517 512
      break
b/lib/constants.py
951 951
# Query resources
952 952
QR_INSTANCE = "instance"
953 953
QR_NODE = "node"
954
QR_LOCK = "lock"
954 955

  
955 956
#: List of resources which can be queried using L{opcodes.OpQuery}
956 957
QR_OP_QUERY = frozenset([QR_INSTANCE, QR_NODE])
957 958

  
958 959
#: List of resources which can be queried using LUXI
959 960
QR_OP_LUXI = QR_OP_QUERY.union([
961
  QR_LOCK,
960 962
  ])
961 963

  
962 964
# Query field types
b/lib/locking.py
32 32
import weakref
33 33
import logging
34 34
import heapq
35
import operator
35 36

  
36 37
from ganeti import errors
37 38
from ganeti import utils
38 39
from ganeti import compat
40
from ganeti import query
39 41

  
40 42

  
41 43
_EXCLUSIVE_TEXT = "exclusive"
42 44
_SHARED_TEXT = "shared"
45
_DELETED_TEXT = "deleted"
43 46

  
44 47
_DEFAULT_PRIORITY = 0
45 48

  
......
432 435
    if monitor:
433 436
      monitor.RegisterLock(self)
434 437

  
435
  def GetInfo(self, fields):
438
  def GetInfo(self, requested):
436 439
    """Retrieves information for querying locks.
437 440

  
438
    @type fields: list of strings
439
    @param fields: List of fields to return
441
    @type requested: set
442
    @param requested: Requested information, see C{query.LQ_*}
440 443

  
441 444
    """
442 445
    self.__lock.acquire()
443 446
    try:
444
      info = []
445

  
446 447
      # Note: to avoid unintentional race conditions, no references to
447 448
      # modifiable objects should be returned unless they were created in this
448 449
      # function.
449
      for fname in fields:
450
        if fname == "name":
451
          info.append(self.name)
452
        elif fname == "mode":
453
          if self.__deleted:
454
            info.append("deleted")
455
            assert not (self.__exc or self.__shr)
456
          elif self.__exc:
457
            info.append(_EXCLUSIVE_TEXT)
458
          elif self.__shr:
459
            info.append(_SHARED_TEXT)
460
          else:
461
            info.append(None)
462
        elif fname == "owner":
463
          if self.__exc:
464
            owner = [self.__exc]
465
          else:
466
            owner = self.__shr
467

  
468
          if owner:
469
            assert not self.__deleted
470
            info.append([i.getName() for i in owner])
471
          else:
472
            info.append(None)
473
        elif fname == "pending":
474
          data = []
475

  
476
          # Sorting instead of copying and using heaq functions for simplicity
477
          for (_, prioqueue) in sorted(self.__pending):
478
            for cond in prioqueue:
479
              if cond.shared:
480
                mode = _SHARED_TEXT
481
              else:
482
                mode = _EXCLUSIVE_TEXT
483

  
484
              # This function should be fast as it runs with the lock held.
485
              # Hence not using utils.NiceSort.
486
              data.append((mode, sorted(i.getName()
487
                                        for i in cond.get_waiting())))
488

  
489
          info.append(data)
450
      mode = None
451
      owner_names = None
452

  
453
      if query.LQ_MODE in requested:
454
        if self.__deleted:
455
          mode = _DELETED_TEXT
456
          assert not (self.__exc or self.__shr)
457
        elif self.__exc:
458
          mode = _EXCLUSIVE_TEXT
459
        elif self.__shr:
460
          mode = _SHARED_TEXT
461

  
462
      # Current owner(s) are wanted
463
      if query.LQ_OWNER in requested:
464
        if self.__exc:
465
          owner = [self.__exc]
490 466
        else:
491
          raise errors.OpExecError("Invalid query field '%s'" % fname)
467
          owner = self.__shr
468

  
469
        if owner:
470
          assert not self.__deleted
471
          owner_names = [i.getName() for i in owner]
492 472

  
493
      return info
473
      # Pending acquires are wanted
474
      if query.LQ_PENDING in requested:
475
        pending = []
476

  
477
        # Sorting instead of copying and using heaq functions for simplicity
478
        for (_, prioqueue) in sorted(self.__pending):
479
          for cond in prioqueue:
480
            if cond.shared:
481
              pendmode = _SHARED_TEXT
482
            else:
483
              pendmode = _EXCLUSIVE_TEXT
484

  
485
            # List of names will be sorted in L{query._GetLockPending}
486
            pending.append((pendmode, [i.getName()
487
                                       for i in cond.get_waiting()]))
488
      else:
489
        pending = None
490

  
491
      return (self.name, mode, owner_names, pending)
494 492
    finally:
495 493
      self.__lock.release()
496 494

  
......
1344 1342
                              monitor=self._monitor),
1345 1343
      }
1346 1344

  
1347
  def QueryLocks(self, fields, sync):
1345
  def QueryLocks(self, fields):
1348 1346
    """Queries information from all locks.
1349 1347

  
1350 1348
    See L{LockMonitor.QueryLocks}.
1351 1349

  
1352 1350
    """
1353
    return self._monitor.QueryLocks(fields, sync)
1351
    return self._monitor.QueryLocks(fields)
1352

  
1353
  def OldStyleQueryLocks(self, fields):
1354
    """Queries information from all locks, returning old-style data.
1355

  
1356
    See L{LockMonitor.OldStyleQueryLocks}.
1357

  
1358
    """
1359
    return self._monitor.OldStyleQueryLocks(fields)
1354 1360

  
1355 1361
  def _names(self, level):
1356 1362
    """List the lock names at the given level.
......
1533 1539
    self._locks[lock] = None
1534 1540

  
1535 1541
  @ssynchronized(_LOCK_ATTR)
1536
  def _GetLockInfo(self, fields):
1542
  def _GetLockInfo(self, requested):
1537 1543
    """Get information from all locks while the monitor lock is held.
1538 1544

  
1539 1545
    """
1540
    result = {}
1546
    return [lock.GetInfo(requested) for lock in self._locks.keys()]
1541 1547

  
1542
    for lock in self._locks.keys():
1543
      assert lock.name not in result, "Found duplicate lock name"
1544
      result[lock.name] = lock.GetInfo(fields)
1548
  def _Query(self, fields):
1549
    """Queries information from all locks.
1545 1550

  
1546
    return result
1551
    @type fields: list of strings
1552
    @param fields: List of fields to return
1553

  
1554
    """
1555
    qobj = query.Query(query.LOCK_FIELDS, fields)
1556

  
1557
    # Get all data and sort by name
1558
    lockinfo = utils.NiceSort(self._GetLockInfo(qobj.RequestedData()),
1559
                              key=operator.itemgetter(0))
1560

  
1561
    return (qobj, query.LockQueryData(lockinfo))
1547 1562

  
1548
  def QueryLocks(self, fields, sync):
1563
  def QueryLocks(self, fields):
1549 1564
    """Queries information from all locks.
1550 1565

  
1551 1566
    @type fields: list of strings
1552 1567
    @param fields: List of fields to return
1553
    @type sync: boolean
1554
    @param sync: Whether to operate in synchronous mode
1555 1568

  
1556 1569
    """
1557
    if sync:
1558
      raise NotImplementedError("Synchronous queries are not implemented")
1570
    (qobj, ctx) = self._Query(fields)
1559 1571

  
1560
    # Get all data without sorting
1561
    result = self._GetLockInfo(fields)
1572
    # Prepare query response
1573
    return query.GetQueryResponse(qobj, ctx)
1574

  
1575
  def OldStyleQueryLocks(self, fields):
1576
    """Queries information from all locks, returning old-style data.
1577

  
1578
    @type fields: list of strings
1579
    @param fields: List of fields to return
1580

  
1581
    """
1582
    (qobj, ctx) = self._Query(fields)
1562 1583

  
1563
    # Sort by name
1564
    return [result[name] for name in utils.NiceSort(result.keys())]
1584
    return qobj.OldStyleQuery(ctx)
b/lib/luxi.py
34 34
import time
35 35
import errno
36 36
import logging
37
import warnings
37 38

  
38 39
from ganeti import serializer
39 40
from ganeti import constants
......
543 544
    return self.CallMethod(REQ_QUERY_TAGS, (kind, name))
544 545

  
545 546
  def QueryLocks(self, fields, sync):
547
    warnings.warn("This LUXI call is deprecated and will be removed, use"
548
                  " Query(\"%s\", ...) instead" % constants.QR_LOCK)
546 549
    return self.CallMethod(REQ_QUERY_LOCKS, (fields, sync))
b/lib/query.py
42 42
 IQ_LIVE,
43 43
 IQ_DISKUSAGE) = range(100, 103)
44 44

  
45
(LQ_MODE,
46
 LQ_OWNER,
47
 LQ_PENDING) = range(10, 13)
45 48

  
46 49
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
47 50
TITLE_RE = re.compile(r"^[^\s]+$")
......
1003 1006
  return _PrepareFieldList(fields)
1004 1007

  
1005 1008

  
1009
class LockQueryData:
1010
  """Data container for lock data queries.
1011

  
1012
  """
1013
  def __init__(self, lockdata):
1014
    """Initializes this class.
1015

  
1016
    """
1017
    self.lockdata = lockdata
1018

  
1019
  def __iter__(self):
1020
    """Iterate over all locks.
1021

  
1022
    """
1023
    return iter(self.lockdata)
1024

  
1025

  
1026
def _GetLockOwners(_, data):
1027
  """Returns a sorted list of a lock's current owners.
1028

  
1029
  """
1030
  (_, _, owners, _) = data
1031

  
1032
  if owners:
1033
    owners = utils.NiceSort(owners)
1034

  
1035
  return (constants.QRFS_NORMAL, owners)
1036

  
1037

  
1038
def _GetLockPending(_, data):
1039
  """Returns a sorted list of a lock's pending acquires.
1040

  
1041
  """
1042
  (_, _, _, pending) = data
1043

  
1044
  if pending:
1045
    pending = [(mode, utils.NiceSort(names))
1046
               for (mode, names) in pending]
1047

  
1048
  return (constants.QRFS_NORMAL, pending)
1049

  
1050

  
1051
def _BuildLockFields():
1052
  """Builds list of fields for lock queries.
1053

  
1054
  """
1055
  return _PrepareFieldList([
1056
    (_MakeField("name", "Name", constants.QFT_TEXT), None,
1057
     lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1058
    (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1059
     lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1060
    (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1061
     _GetLockOwners),
1062
    (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1063
     _GetLockPending),
1064
    ])
1065

  
1066

  
1006 1067
#: Fields available for node queries
1007 1068
NODE_FIELDS = _BuildNodeFields()
1008 1069

  
1009 1070
#: Fields available for instance queries
1010 1071
INSTANCE_FIELDS = _BuildInstanceFields()
1072

  
1073
#: Fields available for lock queries
1074
LOCK_FIELDS = _BuildLockFields()
b/lib/server/masterd.py
56 56
from ganeti import bootstrap
57 57
from ganeti import netutils
58 58
from ganeti import objects
59
from ganeti import query
59 60

  
60 61

  
61 62
CLIENT_REQUEST_WORKERS = 16
......
234 235
      if req.what in constants.QR_OP_QUERY:
235 236
        result = self._Query(opcodes.OpQuery(what=req.what, fields=req.fields,
236 237
                                             filter=req.filter))
238
      elif req.what == constants.QR_LOCK:
239
        if req.filter is not None:
240
          raise errors.OpPrereqError("Lock queries can't be filtered")
241
        return self.server.context.glm.QueryLocks(req.fields)
237 242
      elif req.what in constants.QR_OP_LUXI:
238 243
        raise NotImplementedError
239 244
      else:
......
248 253
      if req.what in constants.QR_OP_QUERY:
249 254
        result = self._Query(opcodes.OpQueryFields(what=req.what,
250 255
                                                   fields=req.fields))
256
      elif req.what == constants.QR_LOCK:
257
        return query.QueryFields(query.LOCK_FIELDS, req.fields)
251 258
      elif req.what in constants.QR_OP_LUXI:
252 259
        raise NotImplementedError
253 260
      else:
......
323 330
    elif method == luxi.REQ_QUERY_LOCKS:
324 331
      (fields, sync) = args
325 332
      logging.info("Received locks query request")
326
      return self.server.context.glm.QueryLocks(fields, sync)
333
      if sync:
334
        raise NotImplementedError("Synchronous queries are not implemented")
335
      return self.server.context.glm.OldStyleQueryLocks(fields)
327 336

  
328 337
    elif method == luxi.REQ_QUEUE_SET_DRAIN_FLAG:
329 338
      drain_flag = args
b/test/ganeti.locking_unittest.py
30 30
import random
31 31
import itertools
32 32

  
33
from ganeti import constants
33 34
from ganeti import locking
34 35
from ganeti import errors
35 36
from ganeti import utils
36 37
from ganeti import compat
38
from ganeti import objects
39
from ganeti import query
37 40

  
38 41
import testutils
39 42

  
......
773 776
    prev.wait()
774 777

  
775 778
    # Check lock information
776
    self.assertEqual(self.sl.GetInfo(["name"]), [self.sl.name])
777
    self.assertEqual(self.sl.GetInfo(["mode", "owner"]),
778
                     ["exclusive", [threading.currentThread().getName()]])
779
    self.assertEqual(self.sl.GetInfo(["name", "pending"]),
780
                     [self.sl.name,
781
                      [(["exclusive", "shared"][int(bool(shared))],
782
                        sorted([t.getName() for t in threads]))
783
                       for acquires in [perprio[i]
784
                                        for i in sorted(perprio.keys())]
785
                       for (shared, _, threads) in acquires]])
779
    self.assertEqual(self.sl.GetInfo(set()), (self.sl.name, None, None, None))
780
    self.assertEqual(self.sl.GetInfo(set([query.LQ_MODE, query.LQ_OWNER])),
781
                     (self.sl.name, "exclusive",
782
                      [threading.currentThread().getName()], None))
783

  
784
    self._VerifyPrioPending(self.sl.GetInfo(set([query.LQ_PENDING])), perprio)
786 785

  
787 786
    # Let threads acquire the lock
788 787
    self.sl.release()
......
803 802

  
804 803
    self.assertRaises(Queue.Empty, self.done.get_nowait)
805 804

  
805
  def _VerifyPrioPending(self, (name, mode, owner, pending), perprio):
806
    self.assertEqual(name, self.sl.name)
807
    self.assert_(mode is None)
808
    self.assert_(owner is None)
809

  
810
    self.assertEqual([(pendmode, sorted(waiting))
811
                      for (pendmode, waiting) in pending],
812
                     [(["exclusive", "shared"][int(bool(shared))],
813
                       sorted(t.getName() for t in threads))
814
                      for acquires in [perprio[i]
815
                                       for i in sorted(perprio.keys())]
816
                      for (shared, _, threads) in acquires])
817

  
806 818

  
807 819
class TestSharedLockInCondition(_ThreadedTestCase):
808 820
  """SharedLock as a condition lock tests"""
......
1638 1650
      locks.append(locking.SharedLock(name, monitor=self.lm))
1639 1651

  
1640 1652
    self.assertEqual(len(self.lm._locks), len(locks))
1641

  
1642
    self.assertEqual(len(self.lm.QueryLocks(["name"], False)),
1643
                     100)
1653
    result = objects.QueryResponse.FromDict(self.lm.QueryLocks(["name"]))
1654
    self.assertEqual(len(result.fields), 1)
1655
    self.assertEqual(len(result.data), 100)
1644 1656

  
1645 1657
    # Delete all locks
1646 1658
    del locks[:]
......
1684 1696
    # Check order in which locks were added
1685 1697
    self.assertEqual([i.name for i in locks], expnames)
1686 1698

  
1687
    # Sync queries are not supported
1688
    self.assertRaises(NotImplementedError, self.lm.QueryLocks, ["name"], True)
1689

  
1690 1699
    # Check query result
1691
    self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner", "pending"],
1692
                                        False),
1693
                     [[name, None, None, []]
1700
    result = self.lm.QueryLocks(["name", "mode", "owner", "pending"])
1701
    self.assert_(isinstance(result, dict))
1702
    response = objects.QueryResponse.FromDict(result)
1703
    self.assertEqual(response.data,
1704
                     [[(constants.QRFS_NORMAL, name),
1705
                       (constants.QRFS_NORMAL, None),
1706
                       (constants.QRFS_NORMAL, None),
1707
                       (constants.QRFS_NORMAL, [])]
1694 1708
                      for name in utils.NiceSort(expnames)])
1709
    self.assertEqual(len(response.fields), 4)
1710
    self.assertEqual(["name", "mode", "owner", "pending"],
1711
                     [fdef.name for fdef in response.fields])
1695 1712

  
1696 1713
    # Test exclusive acquire
1697 1714
    for tlock in locks[::4]:
......
1699 1716
      try:
1700 1717
        def _GetExpResult(name):
1701 1718
          if tlock.name == name:
1702
            return [name, "exclusive", [threading.currentThread().getName()],
1703
                    []]
1704
          return [name, None, None, []]
1705

  
1706
        self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner",
1707
                                             "pending"], False),
1719
            return [(constants.QRFS_NORMAL, name),
1720
                    (constants.QRFS_NORMAL, "exclusive"),
1721
                    (constants.QRFS_NORMAL,
1722
                     [threading.currentThread().getName()]),
1723
                    (constants.QRFS_NORMAL, [])]
1724
          return [(constants.QRFS_NORMAL, name),
1725
                  (constants.QRFS_NORMAL, None),
1726
                  (constants.QRFS_NORMAL, None),
1727
                  (constants.QRFS_NORMAL, [])]
1728

  
1729
        result = self.lm.QueryLocks(["name", "mode", "owner", "pending"])
1730
        self.assertEqual(objects.QueryResponse.FromDict(result).data,
1708 1731
                         [_GetExpResult(name)
1709 1732
                          for name in utils.NiceSort(expnames)])
1710 1733
      finally:
......
1756 1779
            i.wait()
1757 1780

  
1758 1781
          # Check query result
1759
          for (name, mode, owner) in self.lm.QueryLocks(["name", "mode",
1760
                                                         "owner"], False):
1761
            if name == tlock1.name:
1762
              self.assertEqual(mode, "shared")
1763
              self.assertEqual(set(owner), set(i.getName() for i in tthreads1))
1782
          result = self.lm.QueryLocks(["name", "mode", "owner"])
1783
          response = objects.QueryResponse.FromDict(result)
1784
          for (name, mode, owner) in response.data:
1785
            (name_status, name_value) = name
1786
            (owner_status, owner_value) = owner
1787

  
1788
            self.assertEqual(name_status, constants.QRFS_NORMAL)
1789
            self.assertEqual(owner_status, constants.QRFS_NORMAL)
1790

  
1791
            if name_value == tlock1.name:
1792
              self.assertEqual(mode, (constants.QRFS_NORMAL, "shared"))
1793
              self.assertEqual(set(owner_value),
1794
                               set(i.getName() for i in tthreads1))
1764 1795
              continue
1765 1796

  
1766
            if name == tlock2.name:
1767
              self.assertEqual(mode, "shared")
1768
              self.assertEqual(owner, [tthread2.getName()])
1797
            if name_value == tlock2.name:
1798
              self.assertEqual(mode, (constants.QRFS_NORMAL, "shared"))
1799
              self.assertEqual(owner_value, [tthread2.getName()])
1769 1800
              continue
1770 1801

  
1771
            if name == tlock3.name:
1772
              self.assertEqual(mode, "exclusive")
1773
              self.assertEqual(owner, [tthread3.getName()])
1802
            if name_value == tlock3.name:
1803
              self.assertEqual(mode, (constants.QRFS_NORMAL, "exclusive"))
1804
              self.assertEqual(owner_value, [tthread3.getName()])
1774 1805
              continue
1775 1806

  
1776
            self.assert_(name in expnames)
1777
            self.assert_(mode is None)
1778
            self.assert_(owner is None)
1807
            self.assert_(name_value in expnames)
1808
            self.assertEqual(mode, (constants.QRFS_NORMAL, None))
1809
            self.assert_(owner_value is None)
1779 1810

  
1780 1811
          # Release locks again
1781 1812
          releaseev.set()
1782 1813

  
1783 1814
          self._waitThreads()
1784 1815

  
1785
          self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner"], False),
1786
                           [[name, None, None]
1816
          result = self.lm.QueryLocks(["name", "mode", "owner"])
1817
          self.assertEqual(objects.QueryResponse.FromDict(result).data,
1818
                           [[(constants.QRFS_NORMAL, name),
1819
                             (constants.QRFS_NORMAL, None),
1820
                             (constants.QRFS_NORMAL, None)]
1787 1821
                            for name in utils.NiceSort(expnames)])
1788 1822

  
1789 1823
  def testDelete(self):
1790 1824
    lock = locking.SharedLock("TestLock", monitor=self.lm)
1791 1825

  
1792 1826
    self.assertEqual(len(self.lm._locks), 1)
1793
    self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner"], False),
1794
                     [[lock.name, None, None]])
1827
    result = self.lm.QueryLocks(["name", "mode", "owner"])
1828
    self.assertEqual(objects.QueryResponse.FromDict(result).data,
1829
                     [[(constants.QRFS_NORMAL, lock.name),
1830
                       (constants.QRFS_NORMAL, None),
1831
                       (constants.QRFS_NORMAL, None)]])
1795 1832

  
1796 1833
    lock.delete()
1797 1834

  
1798
    self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner"], False),
1799
                     [[lock.name, "deleted", None]])
1835
    result = self.lm.QueryLocks(["name", "mode", "owner"])
1836
    self.assertEqual(objects.QueryResponse.FromDict(result).data,
1837
                     [[(constants.QRFS_NORMAL, lock.name),
1838
                       (constants.QRFS_NORMAL, "deleted"),
1839
                       (constants.QRFS_NORMAL, None)]])
1800 1840
    self.assertEqual(len(self.lm._locks), 1)
1801 1841

  
1802 1842
  def testPending(self):
......
1815 1855
      lock.acquire()
1816 1856
      try:
1817 1857
        self.assertEqual(len(self.lm._locks), 1)
1818
        self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner"], False),
1819
                         [[lock.name, "exclusive",
1820
                           [threading.currentThread().getName()]]])
1858
        result = self.lm.QueryLocks(["name", "mode", "owner"])
1859
        self.assertEqual(objects.QueryResponse.FromDict(result).data,
1860
                         [[(constants.QRFS_NORMAL, lock.name),
1861
                           (constants.QRFS_NORMAL, "exclusive"),
1862
                           (constants.QRFS_NORMAL,
1863
                            [threading.currentThread().getName()])]])
1821 1864

  
1822 1865
        threads = []
1823 1866

  
......
1843 1886

  
1844 1887
        # All acquires are waiting now
1845 1888
        if shared:
1846
          pending = [("shared", sorted([t.getName() for t in threads]))]
1889
          pending = [("shared", utils.NiceSort(t.getName() for t in threads))]
1847 1890
        else:
1848 1891
          pending = [("exclusive", [t.getName()]) for t in threads]
1849 1892

  
1850
        self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner",
1851
                                             "pending"], False),
1852
                         [[lock.name, "exclusive",
1853
                           [threading.currentThread().getName()],
1854
                           pending]])
1893
        result = self.lm.QueryLocks(["name", "mode", "owner", "pending"])
1894
        self.assertEqual(objects.QueryResponse.FromDict(result).data,
1895
                         [[(constants.QRFS_NORMAL, lock.name),
1896
                           (constants.QRFS_NORMAL, "exclusive"),
1897
                           (constants.QRFS_NORMAL,
1898
                            [threading.currentThread().getName()]),
1899
                           (constants.QRFS_NORMAL, pending)]])
1855 1900

  
1856 1901
        self.assertEqual(len(self.lm._locks), 1)
1857 1902
      finally:
......
1860 1905
      self._waitThreads()
1861 1906

  
1862 1907
      # No pending acquires
1863
      self.assertEqual(self.lm.QueryLocks(["name", "mode", "owner", "pending"],
1864
                                          False),
1865
                       [[lock.name, None, None, []]])
1908
      result = self.lm.QueryLocks(["name", "mode", "owner", "pending"])
1909
      self.assertEqual(objects.QueryResponse.FromDict(result).data,
1910
                       [[(constants.QRFS_NORMAL, lock.name),
1911
                         (constants.QRFS_NORMAL, None),
1912
                         (constants.QRFS_NORMAL, None),
1913
                         (constants.QRFS_NORMAL, [])]])
1866 1914

  
1867 1915
      self.assertEqual(len(self.lm._locks), 1)
1868 1916

  

Also available in: Unified diff