Non-redundant instances need to be moved to a different node
before maintenance of the node. Even though they can be moved to
any node, there must be enough capacity to host the instances of the
reboot group to be evacuated.

This is achieved by greedily moving the non-redundant instances
to other nodes, till we run out of capacity. In this way we
refine the groups obtained by coloring the drbd-induced graph.

Signed-off-by: Klaus Aehlig <[email protected]>
---
 src/Ganeti/HTools/Program/Hroller.hs | 69 ++++++++++++++++++++++++++++++++++--
 1 file changed, 66 insertions(+), 3 deletions(-)

diff --git a/src/Ganeti/HTools/Program/Hroller.hs 
b/src/Ganeti/HTools/Program/Hroller.hs
index 88b3c34..f567ba5 100644
--- a/src/Ganeti/HTools/Program/Hroller.hs
+++ b/src/Ganeti/HTools/Program/Hroller.hs
@@ -39,13 +39,16 @@ import qualified Data.IntMap as IntMap
 
 import qualified Ganeti.HTools.Container as Container
 import qualified Ganeti.HTools.Node as Node
+import qualified Ganeti.HTools.Instance as Instance
 import qualified Ganeti.HTools.Group as Group
 
+import Ganeti.BasicTypes
 import Ganeti.Common
 import Ganeti.HTools.CLI
 import Ganeti.HTools.ExtLoader
 import Ganeti.HTools.Graph
 import Ganeti.HTools.Loader
+import Ganeti.HTools.Types
 import Ganeti.Utils
 
 -- | Options list and functions.
@@ -74,6 +77,60 @@ options = do
 arguments :: [ArgCompletion]
 arguments = []
 
+-- | Compute the result of moving an instance to a different node.
+move :: Idx -> Ndx -> (Node.List, Instance.List)
+        -> OpResult (Node.List, Instance.List)
+move idx new_ndx (nl, il) = do
+  let new_node = Container.find new_ndx nl
+      inst = Container.find idx il
+      old_ndx = Instance.pNode inst
+      old_node = Container.find old_ndx nl
+  new_node' <- Node.addPriEx True new_node inst
+  let old_node' = Node.removePri old_node inst
+      inst' = Instance.setPri inst new_ndx
+      nl' = Container.addTwo old_ndx old_node' new_ndx new_node' nl
+      il' = Container.add idx inst' il
+  return (nl', il')
+
+-- | Move an instance to one of the candidate nodes mentioned.
+locateInstance :: Idx -> [Ndx] -> (Node.List, Instance.List)
+                  -> Result (Node.List, Instance.List)
+locateInstance idx ndxs conf =
+  msum $ map (opToResult . flip (move idx) conf) ndxs
+
+-- | Move a list of instances to some of the candidate nodes mentioned.
+locateInstances :: [Idx] -> [Ndx] -> (Node.List, Instance.List)
+                   -> Result (Node.List, Instance.List)
+locateInstances idxs ndxs conf =
+  foldM (\ cf idx -> locateInstance idx ndxs cf) conf idxs
+
+-- | Greedily move the non-redundant instances away from a list of nodes.
+-- The arguments are the list of nodes to be cleared, a list of nodes the
+-- instances can be moved to, and an initial configuration. Returned is a
+-- list of nodes that can be cleared simultaneously and the configuration
+-- after these nodes are cleared.
+clearNodes :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
+              -> Result ([Ndx], (Node.List, Instance.List))
+clearNodes [] _ conf = return ([], conf)
+clearNodes (ndx:ndxs) targets conf = withFirst `mplus` withoutFirst where
+  withFirst = do
+     let othernodes = delete ndx targets
+     conf' <- locateInstances (nonRedundant conf ndx) othernodes conf
+     (ndxs', conf'') <- clearNodes ndxs othernodes conf'
+     return (ndx:ndxs', conf'')
+  withoutFirst = clearNodes ndxs targets conf
+
+-- | Parition a list of nodes into chunks according cluster capacity.
+partitionNonRedundant :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
+                         -> Result [[Ndx]]
+partitionNonRedundant [] _ _ = return []
+partitionNonRedundant ndxs targets conf = do
+  (grp, _) <- clearNodes ndxs targets conf
+  guard . not . null $ grp
+  let remaining = ndxs \\ grp
+  part <- partitionNonRedundant remaining targets conf
+  return $ grp : part
+
 -- | Gather statistics for the coloring algorithms.
 -- Returns a string with a summary on how each algorithm has performed,
 -- in order of non-decreasing effectiveness, and whether it tied or lost
@@ -179,10 +236,16 @@ main opts args = do
       colorings = map (\(v,a) -> (v,(colorVertMap.a) nodeGraph)) 
colorAlgorithms
       smallestColoring =
         (snd . minimumBy (comparing (IntMap.size . snd))) colorings
-      idToNode = (`Container.find` nodes)
+      allNdx = map Node.idx $ Container.elems nlf
+      splitted = mapM (\ grp -> partitionNonRedundant grp allNdx (nlf,ilf)) $
+                 IntMap.elems smallestColoring
+  rebootGroups <- case splitted of
+                    Ok splitgroups -> return $ concat splitgroups
+                    Bad _ -> exitErr "Not enough capacity to move 
non-redundant\ 
+                                     \ instances"
+  let idToNode = (`Container.find` nodes)
       nodesRebootGroups =
-        map (map idToNode . filter (`IntMap.member` nodes)) $
-        IntMap.elems smallestColoring
+        map (map idToNode . filter (`IntMap.member` nodes)) rebootGroups
       outputRebootGroups = masterLast .
                            sortBy (flip compare `on` length) $
                            nodesRebootGroups
-- 
1.8.2.1

Reply via email to