Revision 1a9eb17e

b/htools/Ganeti/HTools/Program/Hroller.hs
29 29
  , arguments
30 30
  ) where
31 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

  
32 41
import Ganeti.Common
33 42
import Ganeti.HTools.CLI
43
import Ganeti.HTools.ExtLoader
44
import Ganeti.HTools.Graph
45
import Ganeti.HTools.Loader
46
import Ganeti.Utils
34 47

  
35 48
-- | Options list and functions.
36 49
options :: IO [OptType]
......
52 65
arguments :: [ArgCompletion]
53 66
arguments = []
54 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

  
55 86
-- | Main function.
56 87
main :: Options -> [String] -> IO ()
57
main _ _ = return ()
88
main opts args = do
89
  unless (null args) $ exitErr "This program doesn't take any arguments."
90

  
91
  let verbose = optVerbose opts
92

  
93
  -- Load cluster data. The last two arguments, cluster tags and ipolicy, are
94
  -- currently not used by this tool.
95
  ini_cdata@(ClusterData _ fixed_nl ilf _ _) <- loadExternalData opts
96

  
97
  nlf <- setNodeStatus opts fixed_nl
98

  
99
  maybeSaveData (optSaveCluster opts) "original" "before hroller run" ini_cdata
100

  
101
  -- TODO: only online nodes!
102
  -- TODO: filter by node group
103
  -- TODO: fail if instances are running (with option to warn only)
104
  -- TODO: identify master node, and put it last
105

  
106
  nodeGraph <- case Node.mkNodeGraph nlf ilf of
107
                     Nothing -> exitErr "Cannot create node graph"
108
                     Just g -> return g
109

  
110
  when (verbose > 2) . putStrLn $ "Node Graph: " ++ show nodeGraph
111

  
112
  let colorAlgorithms = [ ("LF", colorLF)
113
                        , ("Dsatur", colorDsatur)
114
                        , ("Dcolor", colorDcolor)
115
                        ]
116
      colorings = map (\(v,a) -> (v,(colorVertMap.a) nodeGraph)) colorAlgorithms
117
      smallestColoring =
118
        (snd . minimumBy (comparing (IntMap.size . snd))) colorings
119
      idToName = Node.name  . (`Container.find` nlf)
120
      nodesbycoloring = map (map idToName) $ IntMap.elems smallestColoring
121

  
122
  when (verbose > 1) . putStrLn $ getStats colorings
123

  
124
  unless (optNoHeaders opts) $
125
         putStrLn "'Node Reboot Groups'"
126
  mapM_ (putStrLn . commaJoin) nodesbycoloring
b/man/hroller.rst
35 35
which set of nodes can be rebooted at the same time while avoiding
36 36
having both primary and secondary nodes being rebooted at the same time.
37 37

  
38
ALGORITHM FOR CALCULATING OFFLINE REBOOT GROUPS
39
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40

  
41
hroller will view the nodes as vertices of an undirected graph,
42
connecting by instances which have both a primary and a secondary node.
43
It will then color the graph using a few different heuristics, and
44
return the minimum-size color set found. Node with the same color don't
45
share an edge, and as such don't have an instance with both primary and
46
secondary node on them, so they are safe to be rebooted concurrently.
47

  
38 48
OPTIONS
39 49
-------
40 50

  
......
44 54
BUGS
45 55
----
46 56

  
47
The program does nothing.
57
The master node should be always the last node of the last group, or anyway
58
somehow easily identifiable. Right now this is not done.
59

  
60
Offline nodes should be ignored.
61

  
62
Filtering by nodegroup should be allowed.
63

  
64
If instances are online the tool should refuse to do offline rolling
65
maintenances, unless explicitely requested.
66

  
67
End-to-end shelltests should be provided.
68

  
69
Online rolling maintenances (where instance need not be shut down, but
70
are migrated from node to node) are not supported yet. Hroller by design
71
should support them both with and without secondary node replacement.
72

  
73
EXAMPLE
74
-------
75

  
76
Note that these examples may not for the latest version.
77

  
78
Offline Rolling node reboot output
79
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80

  
81
With the default options, the program shows one reboot group per line as
82
a comma-separated list.
83

  
84
    $ hroller
85
    'Node Reboot Groups'
86
    node1.example.com,node3.example.com,node5.example.com
87
    node8.example.com,node6.example.com,node2.example.com
88
    node7.example.com,node4.example.com
48 89

  
49 90
.. vim: set textwidth=72 :
50 91
.. Local Variables:

Also available in: Unified diff