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"])