78b650bae011f56fca6559553453f773ff444dbe
[ganeti-local] / src / Ganeti / HTools / Program / Hroller.hs
1 {-| Cluster rolling maintenance helper.
2
3 -}
4
5 {-
6
7 Copyright (C) 2012 Google Inc.
8
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.
13
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.
18
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
22 02110-1301, USA.
23
24 -}
25
26 module Ganeti.HTools.Program.Hroller
27   ( main
28   , options
29   , arguments
30   ) where
31
32 import Control.Monad
33 import Data.List
34 import Data.Ord
35
36 import qualified Data.IntMap as IntMap
37
38 import qualified Ganeti.HTools.Container as Container
39 import qualified Ganeti.HTools.Node as Node
40
41 import Ganeti.Common
42 import Ganeti.HTools.CLI
43 import Ganeti.HTools.ExtLoader
44 import Ganeti.HTools.Graph
45 import Ganeti.HTools.Loader
46 import Ganeti.Utils
47
48 -- | Options list and functions.
49 options :: IO [OptType]
50 options = do
51   luxi <- oLuxiSocket
52   return
53     [ luxi
54     , oRapiMaster
55     , oDataFile
56     , oIAllocSrc
57     , oOfflineNode
58     , oVerbose
59     , oQuiet
60     , oNoHeaders
61     , oSaveCluster
62     ]
63
64 -- | The list of arguments supported by the program.
65 arguments :: [ArgCompletion]
66 arguments = []
67
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) ++ " "
76           grpsizes 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)
81           helper el (old, str)
82             | old == elsize = (elsize, str ++ " TIE " ++ algostat el)
83             | otherwise = (elsize, str ++ " LOOSE " ++ algostat el)
84               where elsize = (IntMap.size.snd) el
85
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]]
90 filterOutput l =
91   let onlineOnly = filter (not . Node.offline)
92       nonNullOnly = filter (not . null)
93   in nonNullOnly (map onlineOnly l)
94
95 -- | Main function.
96 main :: Options -> [String] -> IO ()
97 main opts args = do
98   unless (null args) $ exitErr "This program doesn't take any arguments."
99
100   let verbose = optVerbose opts
101
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
105
106   nlf <- setNodeStatus opts fixed_nl
107
108   maybeSaveData (optSaveCluster opts) "original" "before hroller run" ini_cdata
109
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
113
114   nodeGraph <- case Node.mkNodeGraph nlf ilf of
115                      Nothing -> exitErr "Cannot create node graph"
116                      Just g -> return g
117
118   when (verbose > 2) . putStrLn $ "Node Graph: " ++ show nodeGraph
119
120   let colorAlgorithms = [ ("LF", colorLF)
121                         , ("Dsatur", colorDsatur)
122                         , ("Dcolor", colorDcolor)
123                         ]
124       colorings = map (\(v,a) -> (v,(colorVertMap.a) nodeGraph)) colorAlgorithms
125       smallestColoring =
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
131
132   when (verbose > 1) . putStrLn $ getStats colorings
133
134   unless (optNoHeaders opts) $
135          putStrLn "'Node Reboot Groups'"
136   mapM_ (putStrLn . commaJoin) outputRebootNames