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