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 Instance
19 import qualified Cluster
21 -- | Command line options structure.
22 data Options = Options
23 { optShowNodes :: Bool
25 , optNodef :: FilePath
26 , optInstf :: FilePath
28 , optMaxRemovals :: Int
33 -- | Default values for the command line options.
34 defaultOptions :: Options
35 defaultOptions = Options
36 { optShowNodes = False
39 , optInstf = "instances"
46 {- | Start computing the solution at the given depth and recurse until
47 we find a valid solution or we exceed the maximum depth.
50 iterateDepth :: Cluster.NodeList
51 -> [Instance.Instance]
56 -> IO (Maybe Cluster.Solution)
57 iterateDepth nl bad_instances depth max_removals min_delta max_delta =
59 max_depth = length bad_instances
60 sol = Cluster.computeSolution nl bad_instances depth
61 max_removals min_delta max_delta
68 if depth > max_depth then
71 iterateDepth nl bad_instances (depth + 1)
72 max_removals min_delta max_delta
75 -- | Options list and functions
76 options :: [OptDescr (Options -> Options)]
78 [ Option ['p'] ["print-nodes"]
79 (NoArg (\ opts -> opts { optShowNodes = True }))
80 "print the final node list"
81 , Option ['C'] ["print-commands"]
82 (NoArg (\ opts -> opts { optShowCmds = True }))
83 "print the ganeti command list for reaching the solution"
84 , Option ['n'] ["nodes"]
85 (ReqArg (\ f opts -> opts { optNodef = f }) "FILE")
87 , Option ['i'] ["instances"]
88 (ReqArg (\ f opts -> opts { optInstf = f }) "FILE")
89 "the instance list FILE"
90 , Option ['d'] ["depth"]
91 (ReqArg (\ i opts -> opts { optMinDepth = (read i)::Int }) "D")
92 "start computing the solution at depth D"
93 , Option ['r'] ["max-removals"]
94 (ReqArg (\ i opts -> opts { optMaxRemovals = (read i)::Int }) "R")
95 "do not process more than R removal sets (useful for high depths)"
96 , Option ['L'] ["max-delta"]
97 (ReqArg (\ i opts -> opts { optMaxDelta = (read i)::Int }) "L")
98 "refuse solutions with delta higher than L"
99 , Option ['l'] ["min-delta"]
100 (ReqArg (\ i opts -> opts { optMinDelta = (read i)::Int }) "L")
101 "return once a solution with delta L or lower has been found"
104 -- | Command line parser, using the 'options' structure.
105 parseOpts :: [String] -> IO (Options, [String])
107 case getOpt Permute options argv of
109 return (foldl (flip id) defaultOptions o, n)
111 ioError (userError (concat errs ++ usageInfo header options))
112 where header = "Usage: hn1 [OPTION...]"
117 cmd_args <- System.getArgs
118 (opts, _) <- parseOpts cmd_args
119 let min_depth = optMinDepth opts
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 (null bad_instances) $ do
131 putStrLn "Happy time! Cluster is fine, no need to burn CPU."
134 when (length bad_instances < min_depth) $ do
135 printf "Error: depth %d is higher than the number of bad instances.\n"
137 exitWith $ ExitFailure 2
139 putStr "Computing solution: depth "
142 result <- iterateDepth nl bad_instances min_depth (optMaxRemovals opts)
143 (optMinDelta opts) (optMaxDelta opts)
144 let (min_d, solution) =
146 Just (Cluster.Solution a b) -> (a, b)
148 when (min_d == -1) $ do
149 putStrLn "failed. Try to run with higher depth."
150 exitWith $ ExitFailure 1
152 printf "found.\nSolution (delta=%d):\n" $! min_d
153 let (sol_strs, cmd_strs) = Cluster.printSolution il ktn kti solution
154 putStr $ unlines $ sol_strs
155 when (optShowCmds opts) $
158 putStrLn "Commands to run to reach the above solution:"
159 putStr $ unlines $ map (" echo gnt-instance " ++) $ concat cmd_strs
160 when (optShowNodes opts) $
162 let (orig_mem, orig_disk) = Cluster.totalResources nl
163 ns = Cluster.applySolution nl il solution
164 (final_mem, final_disk) = Cluster.totalResources ns
166 putStrLn "Final cluster status:"
167 putStrLn $ Cluster.printNodes ktn ns
168 printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
169 printf "Final: mem=%d disk=%d\n" final_mem final_disk