1 {-| Cluster rolling maintenance helper.
7 Copyright (C) 2012 Google Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 module Ganeti.HTools.Program.Hroller
36 import qualified Data.IntMap as IntMap
38 import qualified Ganeti.HTools.Container as Container
39 import qualified Ganeti.HTools.Node as Node
42 import Ganeti.HTools.CLI
43 import Ganeti.HTools.ExtLoader
44 import Ganeti.HTools.Graph
45 import Ganeti.HTools.Loader
48 -- | Options list and functions.
49 options :: IO [OptType]
64 -- | The list of arguments supported by the program.
65 arguments :: [ArgCompletion]
68 -- | Gather statistics for the coloring algorithms.
69 -- Returns a string with a summary on how each algorithm has performed,
70 -- in order of non-decreasing effectiveness, and whether it tied or lost
71 -- with the previous one.
72 getStats :: [(String, ColorVertMap)] -> String
73 getStats colorings = snd . foldr helper (0,"") $ algBySize colorings
74 where algostat (algo, cmap) = algo ++ ": " ++ size cmap ++ grpsizes cmap
75 size cmap = show (IntMap.size cmap) ++ " "
77 "(" ++ commaJoin (map (show.length) (IntMap.elems cmap)) ++ ")"
78 algBySize = sortBy (flip (comparing (IntMap.size.snd)))
79 helper :: (String, ColorVertMap) -> (Int, String) -> (Int, String)
80 helper el (0, _) = ((IntMap.size.snd) el, algostat el)
82 | old == elsize = (elsize, str ++ " TIE " ++ algostat el)
83 | otherwise = (elsize, str ++ " LOOSE " ++ algostat el)
84 where elsize = (IntMap.size.snd) el
86 -- | Filter the output list.
87 -- Only online nodes are shown, and output groups which are empty after being
88 -- filtered are removed as well.
89 filterOutput :: [[Node.Node]] -> [[Node.Node]]
91 let onlineOnly = filter (not . Node.offline)
92 nonNullOnly = filter (not . null)
93 in nonNullOnly (map onlineOnly l)
96 main :: Options -> [String] -> IO ()
98 unless (null args) $ exitErr "This program doesn't take any arguments."
100 let verbose = optVerbose opts
102 -- Load cluster data. The last two arguments, cluster tags and ipolicy, are
103 -- currently not used by this tool.
104 ini_cdata@(ClusterData _ fixed_nl ilf _ _) <- loadExternalData opts
106 nlf <- setNodeStatus opts fixed_nl
108 maybeSaveData (optSaveCluster opts) "original" "before hroller run" ini_cdata
110 -- TODO: filter by node group
111 -- TODO: fail if instances are running (with option to warn only)
112 -- TODO: identify master node, and put it last
114 nodeGraph <- case Node.mkNodeGraph nlf ilf of
115 Nothing -> exitErr "Cannot create node graph"
118 when (verbose > 2) . putStrLn $ "Node Graph: " ++ show nodeGraph
120 let colorAlgorithms = [ ("LF", colorLF)
121 , ("Dsatur", colorDsatur)
122 , ("Dcolor", colorDcolor)
124 colorings = map (\(v,a) -> (v,(colorVertMap.a) nodeGraph)) colorAlgorithms
126 (snd . minimumBy (comparing (IntMap.size . snd))) colorings
127 idToNode = (`Container.find` nlf)
128 nodesRebootGroups = map (map idToNode) $ IntMap.elems smallestColoring
129 outputRebootGroups = filterOutput nodesRebootGroups
130 outputRebootNames = map (map Node.name) outputRebootGroups
132 when (verbose > 1) . putStrLn $ getStats colorings
134 unless (optNoHeaders opts) $
135 putStrLn "'Node Reboot Groups'"
136 mapM_ (putStrLn . commaJoin) outputRebootNames