diff --git a/htest/Test/Ganeti/OpCodes.hs b/htest/Test/Ganeti/OpCodes.hs
index 3251ee228b29625c8a9d98edb27a4b3c8d4a1a94..a1631e58d0fdeba64c945e61596669402ca47249 100644
--- a/htest/Test/Ganeti/OpCodes.hs
+++ b/htest/Test/Ganeti/OpCodes.hs
@@ -217,7 +217,7 @@ instance Arbitrary OpCodes.OpCode where
           genMaybe (pure []) <*> genMaybe genNodeNameNE <*>
           arbitrary <*> genMaybe genNodeNameNE <*>
           genMaybe genNodeNameNE <*> genMaybe genNameNE <*>
-          arbitrary <*> (genTags >>= mapM mkNonEmpty)
+          arbitrary <*> arbitrary <*> (genTags >>= mapM mkNonEmpty)
       "OP_INSTANCE_MULTI_ALLOC" ->
         OpCodes.OpInstanceMultiAlloc <$> genMaybe genNameNE <*> pure []
       "OP_INSTANCE_REINSTALL" ->
diff --git a/htools/Ganeti/OpCodes.hs b/htools/Ganeti/OpCodes.hs
index 6d93286a28caaeaa06a352954bc291fa51b33b57..03b606997ba48b9ebbdcfa4ee25e0920a7b3f2fe 100644
--- a/htools/Ganeti/OpCodes.hs
+++ b/htools/Ganeti/OpCodes.hs
@@ -294,6 +294,7 @@ $(genOpCode "OpCode"
      , pSrcNode
      , pSrcPath
      , pStartInstance
+     , pOpportunisticLocking
      , pInstTags
      ])
   , ("OpInstanceMultiAlloc",
diff --git a/htools/Ganeti/OpParams.hs b/htools/Ganeti/OpParams.hs
index cda751e392c5db294999b758d9d6d90b5c7b7ef6..d3eca0e4d50397a3fa93520dcf3b901001df9fa8 100644
--- a/htools/Ganeti/OpParams.hs
+++ b/htools/Ganeti/OpParams.hs
@@ -71,6 +71,7 @@ module Ganeti.OpParams
   , pIgnoreConsistency
   , pStorageName
   , pUseLocking
+  , pOpportunisticLocking
   , pNameCheck
   , pNodeGroupAllocPolicy
   , pGroupNodeParams
@@ -607,6 +608,12 @@ pStorageName =
 pUseLocking :: Field
 pUseLocking = defaultFalse "use_locking"
 
+-- | Whether to employ opportunistic locking for nodes, meaning nodes already
+-- locked by another opcode won't be considered for instance allocation (only
+-- when an iallocator is used).
+pOpportunisticLocking :: Field
+pOpportunisticLocking = defaultFalse "opportunistic_locking"
+
 -- | Whether to check name.
 pNameCheck :: Field
 pNameCheck = defaultTrue "name_check"
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 91818d1c61c52e50ed9fb8320430400b5176f2f1..47216ccd4417c80e68a732ea495906ec2670d74b 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -159,6 +159,12 @@ _PDiskParams = \
 _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
 _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
 
+#: Opportunistic locking
+_POpportunisticLocking = \
+  ("opportunistic_locking", False, ht.TBool,
+   ("Whether to employ opportunistic locking for nodes, meaning nodes"
+    " already locked by another opcode won't be considered for instance"
+    " allocation (only when an iallocator is used)"))
 
 _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
                    "Whether to ignore ipolicy violations")
@@ -1241,6 +1247,7 @@ class OpInstanceCreate(OpCode):
     _PWaitForSync,
     _PNameCheck,
     _PIgnoreIpolicy,
+    _POpportunisticLocking,
     ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
     ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
      "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
@@ -1300,6 +1307,7 @@ class OpInstanceMultiAlloc(OpCode):
 
   """
   OP_PARAMS = [
+    _POpportunisticLocking,
     _PIAllocFromDesc("Iallocator used to allocate all the instances"),
     ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
      "List of instance create opcodes describing the instances to allocate"),