1 {-| Solver for N+1 cluster errors
7 Copyright (C) 2009 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 Main (main) where
33 import System.Console.GetOpt
34 import qualified System
36 import Text.Printf (printf)
38 import qualified Ganeti.HTools.Container as Container
39 import qualified Ganeti.HTools.Instance as Instance
40 import qualified Ganeti.HTools.Node as Node
41 import qualified Ganeti.HTools.Cluster as Cluster
42 import qualified Ganeti.HTools.CLI as CLI
44 -- | Command line options structure.
45 data Options = Options
46 { optShowNodes :: Bool
48 , optNodef :: FilePath
49 , optNodeSet :: Bool -- ^ The nodes have been set by options
50 , optInstf :: FilePath -- ^ Path to the instances file
51 , optInstSet :: Bool -- ^ The insts have been set by options
53 , optMaxRemovals :: Int
57 , optShowVer :: Bool -- ^ Just show the program version
58 , optShowHelp :: Bool -- ^ Just show the help
61 instance CLI.CLIOptions Options where
62 showVersion = optShowVer
63 showHelp = optShowHelp
65 instance CLI.EToolOptions Options where
70 masterName = optMaster
73 -- | Default values for the command line options.
74 defaultOptions :: Options
75 defaultOptions = Options
76 { optShowNodes = False
80 , optInstf = "instances"
91 {- | Start computing the solution at the given depth and recurse until
92 we find a valid solution or we exceed the maximum depth.
95 iterateDepth :: Node.List
96 -> [Instance.Instance]
101 -> IO (Maybe Cluster.Solution)
102 iterateDepth nl bad_instances depth max_removals min_delta max_delta =
104 max_depth = length bad_instances
105 sol = Cluster.computeSolution nl bad_instances depth
106 max_removals min_delta max_delta
111 case sol `seq` sol of
113 if depth > max_depth then
116 iterateDepth nl bad_instances (depth + 1)
117 max_removals min_delta max_delta
120 -- | Options list and functions
121 options :: [OptDescr (Options -> Options)]
123 [ Option ['p'] ["print-nodes"]
124 (NoArg (\ opts -> opts { optShowNodes = True }))
125 "print the final node list"
126 , Option ['C'] ["print-commands"]
127 (NoArg (\ opts -> opts { optShowCmds = True }))
128 "print the ganeti command list for reaching the solution"
129 , Option ['n'] ["nodes"]
130 (ReqArg (\ f opts -> opts { optNodef = f, optNodeSet = True }) "FILE")
132 , Option ['i'] ["instances"]
133 (ReqArg (\ f opts -> opts { optInstf = f, optInstSet = True }) "FILE")
134 "the instance list FILE"
135 , Option ['d'] ["depth"]
136 (ReqArg (\ i opts -> opts { optMinDepth = (read i)::Int }) "D")
137 "start computing the solution at depth D"
138 , Option ['r'] ["max-removals"]
139 (ReqArg (\ i opts -> opts { optMaxRemovals = (read i)::Int }) "R")
140 "do not process more than R removal sets (useful for high depths)"
141 , Option ['L'] ["max-delta"]
142 (ReqArg (\ i opts -> opts { optMaxDelta = (read i)::Int }) "L")
143 "refuse solutions with delta higher than L"
144 , Option ['l'] ["min-delta"]
145 (ReqArg (\ i opts -> opts { optMinDelta = (read i)::Int }) "L")
146 "return once a solution with delta L or lower has been found"
147 , Option ['m'] ["master"]
148 (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
149 "collect data via RAPI at the given ADDRESS"
150 , Option ['V'] ["version"]
151 (NoArg (\ opts -> opts { optShowVer = True}))
152 "show the version of the program"
153 , Option ['h'] ["help"]
154 (NoArg (\ opts -> opts { optShowHelp = True}))
161 cmd_args <- System.getArgs
162 (opts, args) <- CLI.parseOpts cmd_args "hn1" options
165 unless (null args) $ do
166 hPutStrLn stderr "Error: this program doesn't take any arguments."
167 exitWith $ ExitFailure 1
169 (nl, il, csf) <- CLI.loadExternalData opts
171 printf "Loaded %d nodes, %d instances\n"
175 when (length csf > 0) $ do
176 printf "Note: Stripping common suffix of '%s' from names\n" csf
178 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
179 min_depth = optMinDepth opts
181 printf "Initial check done: %d bad nodes, %d bad instances.\n"
182 (length bad_nodes) (length bad_instances)
184 when (null bad_instances) $ do
185 putStrLn "Happy time! Cluster is fine, no need to burn CPU."
188 when (length bad_instances < min_depth) $ do
189 printf "Error: depth %d is higher than the number of bad instances.\n"
191 exitWith $ ExitFailure 2
193 let ini_cv = Cluster.compCV nl
194 printf "Initial coefficients: overall %.8f, %s\n"
195 ini_cv (Cluster.printStats nl)
197 putStr "Computing solution: depth "
200 result <- iterateDepth nl bad_instances min_depth (optMaxRemovals opts)
201 (optMinDelta opts) (optMaxDelta opts)
202 let (min_d, solution) =
204 Just (Cluster.Solution a b) -> (a, reverse b)
206 when (min_d == -1) $ do
207 putStrLn "failed. Try to run with higher depth."
208 exitWith $ ExitFailure 1
213 ns = Cluster.applySolution nl il solution
214 fin_cv = Cluster.compCV ns
216 printf "Final coefficients: overall %.8f, %s\n"
218 (Cluster.printStats ns)
220 printf "Solution (delta=%d):\n" $! min_d
221 let (sol_strs, cmd_strs) = Cluster.printSolution ns il solution
222 putStr $ unlines $ sol_strs
223 when (optShowCmds opts) $
226 putStrLn "Commands to run to reach the above solution:"
227 putStr . Cluster.formatCmds . reverse $ cmd_strs
229 when (optShowNodes opts) $
231 let (orig_mem, orig_disk, _, _, _) = Cluster.totalResources nl
232 (final_mem, final_disk, _, _, _) = Cluster.totalResources ns
234 putStrLn "Final cluster status:"
235 putStrLn $ Cluster.printNodes ns
236 printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
237 printf "Final: mem=%d disk=%d\n" final_mem final_disk