diff --git a/lib/locking.py b/lib/locking.py index 5a02fd5d4ca33cdba52ff072d345ada7c6f23b56..e28f2f0b1128542629cc73a01f4d121e00ce263e 100644 --- a/lib/locking.py +++ b/lib/locking.py @@ -1467,26 +1467,33 @@ class LockSet: return removed -# Locking levels, must be acquired in increasing order. -# Current rules are: -# - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be -# acquired before performing any operation, either in shared or in exclusive -# mode. acquiring the BGL in exclusive mode is discouraged and should be -# avoided. -# - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks. -# If you need more than one node, or more than one instance, acquire them at -# the same time. -LEVEL_CLUSTER = 0 -LEVEL_INSTANCE = 1 -LEVEL_NODEGROUP = 2 -LEVEL_NODE = 3 -#: Level for node resources, used for operations with possibly high impact on -#: the node's disks. -LEVEL_NODE_RES = 4 -LEVEL_NETWORK = 5 +# Locking levels, must be acquired in increasing order. Current rules are: +# - At level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be +# acquired before performing any operation, either in shared or exclusive +# mode. Acquiring the BGL in exclusive mode is discouraged and should be +# avoided.. +# - At levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks. If +# you need more than one node, or more than one instance, acquire them at the +# same time. +# - LEVEL_NODE_RES is for node resources and should be used by operations with +# possibly high impact on the node's disks. +# - LEVEL_NODE_ALLOC blocks instance allocations for the whole cluster +# ("NAL" is the only lock at this level). It should be acquired in shared +# mode when an opcode blocks all or a significant amount of a cluster's +# locks. Opcodes doing instance allocations should acquire in exclusive mode. +# Once the set of acquired locks for an opcode has been reduced to the working +# set, the NAL should be released as well to allow allocations to proceed. +(LEVEL_CLUSTER, + LEVEL_NODE_ALLOC, + LEVEL_INSTANCE, + LEVEL_NODEGROUP, + LEVEL_NODE, + LEVEL_NODE_RES, + LEVEL_NETWORK) = range(0, 7) LEVELS = [ LEVEL_CLUSTER, + LEVEL_NODE_ALLOC, LEVEL_INSTANCE, LEVEL_NODEGROUP, LEVEL_NODE, @@ -1511,11 +1518,15 @@ LEVEL_NAMES = { LEVEL_NODE: "node", LEVEL_NODE_RES: "node-res", LEVEL_NETWORK: "network", + LEVEL_NODE_ALLOC: "node-alloc", } # Constant for the big ganeti lock BGL = "BGL" +#: Node allocation lock +NAL = "NAL" + class GanetiLockManager: """The Ganeti Locking Library @@ -1555,10 +1566,12 @@ class GanetiLockManager: LEVEL_NODEGROUP: LockSet(nodegroups, "nodegroup", monitor=self._monitor), LEVEL_INSTANCE: LockSet(instances, "instance", monitor=self._monitor), LEVEL_NETWORK: LockSet(networks, "network", monitor=self._monitor), + LEVEL_NODE_ALLOC: LockSet([NAL], "node-alloc", monitor=self._monitor), } assert compat.all(ls.name == LEVEL_NAMES[level] - for (level, ls) in self.__keyring.items()) + for (level, ls) in self.__keyring.items()), \ + "Keyring name mismatch" def AddToLockMonitor(self, provider): """Registers a new lock with the monitor. diff --git a/test/ganeti.locking_unittest.py b/test/ganeti.locking_unittest.py index 45c8f6d460dca74d5b112e8f78f12c17ccba2858..04b46fce05eb9545550256359752d47abc312c2b 100755 --- a/test/ganeti.locking_unittest.py +++ b/test/ganeti.locking_unittest.py @@ -1804,6 +1804,7 @@ class TestGanetiLockManager(_ThreadedTestCase): def testLockNames(self): self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(["BGL"])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE_ALLOC), set(["NAL"])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes)) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set(self.nodegroups)) @@ -1816,6 +1817,7 @@ class TestGanetiLockManager(_ThreadedTestCase): locking.GanetiLockManager._instance = None self.GL = locking.GanetiLockManager([], [], [], []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(["BGL"])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE_ALLOC), set(["NAL"])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set()) @@ -1824,6 +1826,7 @@ class TestGanetiLockManager(_ThreadedTestCase): locking.GanetiLockManager._instance = None self.GL = locking.GanetiLockManager(self.nodes, self.nodegroups, [], []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(["BGL"])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE_ALLOC), set(["NAL"])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes)) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set(self.nodegroups)) @@ -1833,6 +1836,7 @@ class TestGanetiLockManager(_ThreadedTestCase): locking.GanetiLockManager._instance = None self.GL = locking.GanetiLockManager([], [], self.instances, []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(["BGL"])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE_ALLOC), set(["NAL"])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), @@ -1841,6 +1845,7 @@ class TestGanetiLockManager(_ThreadedTestCase): locking.GanetiLockManager._instance = None self.GL = locking.GanetiLockManager([], [], [], self.networks) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(["BGL"])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE_ALLOC), set(["NAL"])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set()) @@ -1946,6 +1951,8 @@ class TestGanetiLockManager(_ThreadedTestCase): def testModifiableLevels(self): self.assertRaises(AssertionError, self.GL.add, locking.LEVEL_CLUSTER, ["BGL2"]) + self.assertRaises(AssertionError, self.GL.add, locking.LEVEL_NODE_ALLOC, + ["NAL2"]) self.GL.acquire(locking.LEVEL_CLUSTER, ["BGL"]) self.GL.add(locking.LEVEL_INSTANCE, ["i4"]) self.GL.remove(locking.LEVEL_INSTANCE, ["i3"])