diff --git a/Ganeti/HTools/Cluster.hs b/Ganeti/HTools/Cluster.hs
index f5a956b7d28b2e0bf1e9bce3b80ce70f5b473985..19a0fb0c58d431ea23b9864dc526591238a65205 100644
--- a/Ganeti/HTools/Cluster.hs
+++ b/Ganeti/HTools/Cluster.hs
@@ -61,6 +61,8 @@ module Ganeti.HTools.Cluster
     -- * Allocation functions
     , iterateAlloc
     , tieredAlloc
+    , instanceGroup
+    , findSplitInstances
     ) where
 
 import Data.List
@@ -830,3 +832,27 @@ iMoveToJob nl il idx move =
          ReplaceSecondary ns -> [ opR ns ]
          ReplaceAndFailover np -> [ opR np, opF ]
          FailoverAndReplace ns -> [ opF, opR ns ]
+
+-- | Computes the group of an instance
+instanceGroup :: Node.List -> Instance.Instance -> Result GroupID
+instanceGroup nl i =
+  let sidx = Instance.sNode i
+      pnode = Container.find (Instance.pNode i) nl
+      snode = if sidx == Node.noSecondary
+              then pnode
+              else Container.find sidx nl
+      puuid = Node.group pnode
+      suuid = Node.group snode
+  in if puuid /= suuid
+     then fail ("Instance placed accross two node groups, primary " ++ puuid ++
+                ", secondary " ++ suuid)
+     else return puuid
+
+-- | Compute the list of badly allocated instances (split across node
+-- groups)
+findSplitInstances :: Node.List -> Instance.List -> [Instance.Instance]
+findSplitInstances nl il =
+  filter (not . isOk . instanceGroup nl) (Container.elems il)
+  where isOk x = case x of
+          Bad _ -> False
+          _ -> True
diff --git a/Ganeti/HTools/QC.hs b/Ganeti/HTools/QC.hs
index cd3ae64ea934afcf86e41a5be91f70d0807d9b53..818827d5cbe849b01e50aaa494d9a7d878297c24 100644
--- a/Ganeti/HTools/QC.hs
+++ b/Ganeti/HTools/QC.hs
@@ -704,6 +704,22 @@ prop_ClusterAllocBalance node =
                        tbl = Cluster.Table ynl il' cv []
                    in canBalance tbl True False
 
+-- | Checks consistency
+prop_ClusterCheckConsistency node inst =
+  let nl = makeSmallCluster node 3
+      [node1, node2, node3] = Container.elems nl
+      node3' = node3 { Node.group = "other-uuid" }
+      nl' = Container.add (Node.idx node3') node3' nl
+      inst1 = Instance.setBoth inst (Node.idx node1) (Node.idx node2)
+      inst2 = Instance.setBoth inst (Node.idx node1) Node.noSecondary
+      inst3 = Instance.setBoth inst (Node.idx node1) (Node.idx node3)
+      ccheck = Cluster.findSplitInstances nl' . Container.fromAssocList
+  in null (ccheck [(0, inst1)]) &&
+     null (ccheck [(0, inst2)]) &&
+     (not . null $ ccheck [(0, inst3)])
+
+
+
 testCluster =
     [ run prop_Score_Zero
     , run prop_CStats_sane
@@ -711,6 +727,7 @@ testCluster =
     , run prop_ClusterCanTieredAlloc
     , run prop_ClusterAllocEvac
     , run prop_ClusterAllocBalance
+    , run prop_ClusterCheckConsistency
     ]
 
 -- | Check that opcode serialization is idempotent