Replace frozenset with compat.UniqueFrozenset
[ganeti-local] / test / ganeti.mcpu_unittest.py
index 9f56870..8a997a0 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2009 Google Inc.
+# Copyright (C) 2009, 2011 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 
 
 import unittest
+import itertools
 
+from ganeti import compat
 from ganeti import mcpu
 from ganeti import opcodes
+from ganeti import cmdlib
+from ganeti import locking
+from ganeti import constants
+from ganeti.constants import \
+    LOCK_ATTEMPTS_TIMEOUT, \
+    LOCK_ATTEMPTS_MAXWAIT, \
+    LOCK_ATTEMPTS_MINWAIT
 
 import testutils
 
 
+REQ_BGL_WHITELIST = compat.UniqueFrozenset([
+  opcodes.OpClusterActivateMasterIp,
+  opcodes.OpClusterDeactivateMasterIp,
+  opcodes.OpClusterDestroy,
+  opcodes.OpClusterPostInit,
+  opcodes.OpClusterRename,
+  opcodes.OpInstanceRename,
+  opcodes.OpNodeAdd,
+  opcodes.OpNodeRemove,
+  opcodes.OpTestAllocator,
+  ])
+
+
 class TestLockAttemptTimeoutStrategy(unittest.TestCase):
   def testConstants(self):
     tpa = mcpu.LockAttemptTimeoutStrategy._TIMEOUT_PER_ATTEMPT
-    self.assert_(len(tpa) > 10)
-    self.assert_(sum(tpa) >= 150.0)
+    self.assert_(len(tpa) > LOCK_ATTEMPTS_TIMEOUT / LOCK_ATTEMPTS_MAXWAIT)
+    self.assert_(sum(tpa) >= LOCK_ATTEMPTS_TIMEOUT)
+
+    self.assertTrue(LOCK_ATTEMPTS_TIMEOUT >= 1800,
+                    msg="Waiting less than half an hour per priority")
+    self.assertTrue(LOCK_ATTEMPTS_TIMEOUT <= 3600,
+                    msg="Waiting more than an hour per priority")
 
   def testSimple(self):
     strat = mcpu.LockAttemptTimeoutStrategy(_random_fn=lambda: 0.5,
@@ -45,8 +72,8 @@ class TestLockAttemptTimeoutStrategy(unittest.TestCase):
       timeout = strat.NextAttempt()
       self.assert_(timeout is not None)
 
-      self.assert_(timeout <= 10.0)
-      self.assert_(timeout >= 0.0)
+      self.assert_(timeout <= LOCK_ATTEMPTS_MAXWAIT)
+      self.assert_(timeout >= LOCK_ATTEMPTS_MINWAIT)
       self.assert_(prev is None or timeout >= prev)
 
       prev = timeout
@@ -58,10 +85,210 @@ class TestLockAttemptTimeoutStrategy(unittest.TestCase):
 class TestDispatchTable(unittest.TestCase):
   def test(self):
     for opcls in opcodes.OP_MAPPING.values():
-      if opcls is opcodes.OpCode or opcls is opcodes.OpTestDummy:
+      if not opcls.WITH_LU:
         continue
-      self.assert_(opcls in mcpu.Processor.DISPATCH_TABLE,
-                   msg="%s missing handler class" % opcls)
+      self.assertTrue(opcls in mcpu.Processor.DISPATCH_TABLE,
+                      msg="%s missing handler class" % opcls)
+
+      # Check against BGL whitelist
+      lucls = mcpu.Processor.DISPATCH_TABLE[opcls]
+      if lucls.REQ_BGL:
+        self.assertTrue(opcls in REQ_BGL_WHITELIST,
+                        msg=("%s not whitelisted for BGL" % opcls.OP_ID))
+      else:
+        self.assertFalse(opcls in REQ_BGL_WHITELIST,
+                         msg=("%s whitelisted for BGL, but doesn't use it" %
+                              opcls.OP_ID))
+
+
+class TestProcessResult(unittest.TestCase):
+  def setUp(self):
+    self._submitted = []
+    self._count = itertools.count(200)
+
+  def _Submit(self, jobs):
+    job_ids = [self._count.next() for _ in jobs]
+    self._submitted.extend(zip(job_ids, jobs))
+    return job_ids
+
+  def testNoJobs(self):
+    for i in [object(), [], False, True, None, 1, 929, {}]:
+      self.assertEqual(mcpu._ProcessResult(NotImplemented, NotImplemented, i),
+                       i)
+
+  def testDefaults(self):
+    src = opcodes.OpTestDummy()
+
+    res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
+      opcodes.OpTestDelay(),
+      opcodes.OpTestDelay(),
+      ], [
+      opcodes.OpTestDelay(),
+      ]]))
+
+    self.assertEqual(res, {
+      constants.JOB_IDS_KEY: [200, 201],
+      })
+
+    (_, (op1, op2)) = self._submitted.pop(0)
+    (_, (op3, )) = self._submitted.pop(0)
+    self.assertRaises(IndexError, self._submitted.pop)
+
+    for op in [op1, op2, op3]:
+      self.assertTrue("OP_TEST_DUMMY" in op.comment)
+      self.assertFalse(hasattr(op, "priority"))
+      self.assertFalse(hasattr(op, "debug_level"))
+
+  def testParams(self):
+    src = opcodes.OpTestDummy(priority=constants.OP_PRIO_HIGH,
+                              debug_level=3)
+
+    res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
+      opcodes.OpTestDelay(priority=constants.OP_PRIO_LOW),
+      ], [
+      opcodes.OpTestDelay(comment="foobar", debug_level=10),
+      ]], other=True, value=range(10)))
+
+    self.assertEqual(res, {
+      constants.JOB_IDS_KEY: [200, 201],
+      "other": True,
+      "value": range(10),
+      })
+
+    (_, (op1, )) = self._submitted.pop(0)
+    (_, (op2, )) = self._submitted.pop(0)
+    self.assertRaises(IndexError, self._submitted.pop)
+
+    self.assertEqual(op1.priority, constants.OP_PRIO_LOW)
+    self.assertTrue("OP_TEST_DUMMY" in op1.comment)
+    self.assertEqual(op1.debug_level, 3)
+
+    self.assertEqual(op2.priority, constants.OP_PRIO_HIGH)
+    self.assertEqual(op2.comment, "foobar")
+    self.assertEqual(op2.debug_level, 3)
+
+
+class _FakeLuWithLocks:
+  def __init__(self, needed_locks, share_locks):
+    self.needed_locks = needed_locks
+    self.share_locks = share_locks
+
+
+class _FakeGlm:
+  def __init__(self, owning_nal):
+    self._owning_nal = owning_nal
+
+  def check_owned(self, level, names):
+    assert level == locking.LEVEL_NODE_ALLOC
+    assert names == locking.NAL
+    return self._owning_nal
+
+  def owning_all(self, level):
+    return False
+
+
+class TestVerifyLocks(unittest.TestCase):
+  def testNoLocks(self):
+    lu = _FakeLuWithLocks({}, {})
+    glm = _FakeGlm(False)
+    mcpu._VerifyLocks(lu, glm,
+                      _mode_whitelist=NotImplemented,
+                      _nal_whitelist=NotImplemented)
+
+  def testNotAllSameMode(self):
+    for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
+      lu = _FakeLuWithLocks({
+        level: ["foo"],
+        }, {
+        level: 0,
+        locking.LEVEL_NODE_ALLOC: 0,
+        })
+      glm = _FakeGlm(False)
+      mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
+
+  def testDifferentMode(self):
+    for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
+      lu = _FakeLuWithLocks({
+        level: ["foo"],
+        }, {
+        level: 0,
+        locking.LEVEL_NODE_ALLOC: 1,
+        })
+      glm = _FakeGlm(False)
+      try:
+        mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
+      except AssertionError, err:
+        self.assertTrue("using the same mode as nodes" in str(err))
+      else:
+        self.fail("Exception not raised")
+
+      # Once more with the whitelist
+      mcpu._VerifyLocks(lu, glm, _mode_whitelist=[_FakeLuWithLocks],
+                        _nal_whitelist=[])
+
+  def testSameMode(self):
+    for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
+      lu = _FakeLuWithLocks({
+        level: ["foo"],
+        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
+        }, {
+        level: 1,
+        locking.LEVEL_NODE_ALLOC: 1,
+        })
+      glm = _FakeGlm(True)
+
+      try:
+        mcpu._VerifyLocks(lu, glm, _mode_whitelist=[_FakeLuWithLocks],
+                          _nal_whitelist=[])
+      except AssertionError, err:
+        self.assertTrue("whitelisted to use different modes" in str(err))
+      else:
+        self.fail("Exception not raised")
+
+      # Once more without the whitelist
+      mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
+
+  def testAllWithoutAllocLock(self):
+    for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
+      lu = _FakeLuWithLocks({
+        level: locking.ALL_SET,
+        }, {
+        level: 0,
+        locking.LEVEL_NODE_ALLOC: 0,
+        })
+      glm = _FakeGlm(False)
+      try:
+        mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
+      except AssertionError, err:
+        self.assertTrue("allocation lock must be used if" in str(err))
+      else:
+        self.fail("Exception not raised")
+
+      # Once more with the whitelist
+      mcpu._VerifyLocks(lu, glm, _mode_whitelist=[],
+                        _nal_whitelist=[_FakeLuWithLocks])
+
+  def testAllWithAllocLock(self):
+    for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
+      lu = _FakeLuWithLocks({
+        level: locking.ALL_SET,
+        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
+        }, {
+        level: 0,
+        locking.LEVEL_NODE_ALLOC: 0,
+        })
+      glm = _FakeGlm(True)
+
+      try:
+        mcpu._VerifyLocks(lu, glm, _mode_whitelist=[],
+                          _nal_whitelist=[_FakeLuWithLocks])
+      except AssertionError, err:
+        self.assertTrue("whitelisted for not acquiring" in str(err))
+      else:
+        self.fail("Exception not raised")
+
+      # Once more without the whitelist
+      mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
 
 
 if __name__ == "__main__":