1 {-| Solver for N+1 cluster errors
5 module Main (main) where
12 import System.Console.GetOpt
13 import qualified System
15 import Text.Printf (printf)
17 import qualified Container
18 import qualified Cluster
21 -- | Command line options structure.
22 data Options = Options
23 { optShowNodes :: Bool
25 , optNodef :: FilePath
26 , optInstf :: FilePath
30 -- | Default values for the command line options.
31 defaultOptions :: Options
32 defaultOptions = Options
33 { optShowNodes = False
36 , optInstf = "instances"
40 {- | Start computing the solution at the given depth and recurse until
41 we find a valid solution or we exceed the maximum depth.
44 iterateDepth :: Cluster.Table
45 -> Int -- ^ Current round
46 -> Int -- ^ Max rounds
48 iterateDepth ini_tbl cur_round max_rounds =
49 let Cluster.Table _ ini_il ini_cv ini_plc = ini_tbl
50 all_inst = Container.elems ini_il
51 fin_tbl = Cluster.checkMove ini_tbl all_inst
52 (Cluster.Table _ _ fin_cv fin_plc) = fin_tbl
53 ini_plc_len = length ini_plc
54 fin_plc_len = length fin_plc
55 allowed_next = (max_rounds < 0 || cur_round < max_rounds)
58 printf " - round %d: " cur_round
61 if fin_cv < ini_cv then
62 if not allowed_next then
63 printf "%.8f, %d moves (stopping due to round limit)\n"
65 (fin_plc_len - ini_plc_len)
67 printf "%.8f, %d moves\n" fin_cv
68 (fin_plc_len - ini_plc_len)
70 "no improvement, stopping\n"
73 (if fin_cv < ini_cv then -- this round made success, try deeper
75 then iterateDepth fin_tbl (cur_round + 1) max_rounds
76 -- don't go deeper, but return the better solution
81 -- | Options list and functions
82 options :: [OptDescr (Options -> Options)]
84 [ Option ['p'] ["print-nodes"]
85 (NoArg (\ opts -> opts { optShowNodes = True }))
86 "print the final node list"
87 , Option ['C'] ["print-commands"]
88 (NoArg (\ opts -> opts { optShowCmds = True }))
89 "print the ganeti command list for reaching the solution"
90 , Option ['n'] ["nodes"]
91 (ReqArg (\ f opts -> opts { optNodef = f }) "FILE")
93 , Option ['i'] ["instances"]
94 (ReqArg (\ f opts -> opts { optInstf = f }) "FILE")
95 "the instance list FILE"
96 , Option ['r'] ["max-rounds"]
97 (ReqArg (\ i opts -> opts { optMaxRounds = (read i)::Int }) "N")
98 "do not run for more than R rounds(useful for very unbalanced clusters)"
101 -- | Command line parser, using the 'options' structure.
102 parseOpts :: [String] -> IO (Options, [String])
104 case getOpt Permute options argv of
106 return (foldl (flip id) defaultOptions o, n)
108 ioError (userError (concat errs ++ usageInfo header options))
109 where header = "Usage: hbal [OPTION...]"
114 i <- getInstances "gnta1"
115 n <- getNodes "gnta1"
118 cmd_args <- System.getArgs
119 (opts, _) <- parseOpts cmd_args
120 (nl, il, ktn, kti) <- liftM2 Cluster.loadData
121 (readFile $ optNodef opts)
122 (readFile $ optInstf opts)
123 printf "Loaded %d nodes, %d instances\n"
126 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
127 printf "Initial check done: %d bad nodes, %d bad instances.\n"
128 (length bad_nodes) (length bad_instances)
130 when (length bad_nodes > 0) $ do
131 putStrLn "Cluster is not N+1 happy, please fix N+1 first. Exiting."
132 exitWith $ ExitFailure 1
134 when (optShowNodes opts) $
136 putStrLn "Initial cluster status:"
137 putStrLn $ Cluster.printNodes ktn nl
139 let ini_cv = Cluster.compCV nl
140 ini_tbl = Cluster.Table nl il ini_cv []
141 printf "Initial coefficients: overall %.8f, %s\n"
142 ini_cv (Cluster.printStats nl)
144 putStrLn "Trying to minimize the CV..."
145 fin_tbl <- iterateDepth ini_tbl 1 (optMaxRounds opts)
146 let (Cluster.Table fin_nl _ fin_cv fin_plc) = fin_tbl
147 ord_plc = reverse fin_plc
148 printf "Final coefficients: overall %.8f, %s\n"
150 (Cluster.printStats fin_nl)
152 printf "Solution length=%d\n" (length ord_plc)
154 let (sol_strs, cmd_strs) = Cluster.printSolution il ktn kti ord_plc
155 putStr $ unlines $ sol_strs
156 when (optShowCmds opts) $
159 putStrLn "Commands to run to reach the above solution:"
160 putStr $ unlines $ map (" echo gnt-instance " ++) $ concat cmd_strs
161 when (optShowNodes opts) $
163 let (orig_mem, orig_disk) = Cluster.totalResources nl
164 (final_mem, final_disk) = Cluster.totalResources fin_nl
166 putStrLn "Final cluster status:"
167 putStrLn $ Cluster.printNodes ktn fin_nl
168 printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
169 printf "Final: mem=%d disk=%d\n" final_mem final_disk