import qualified Container
import qualified Cluster
+import qualified Version
+import Rapi
+import Utils
-- | Command line options structure.
data Options = Options
{ optShowNodes :: Bool
, optShowCmds :: Bool
+ , optOneline :: Bool
, optNodef :: FilePath
, optInstf :: FilePath
, optMaxRounds :: Int
+ , optMaster :: String
} deriving Show
-- | Default values for the command line options.
defaultOptions = Options
{ optShowNodes = False
, optShowCmds = False
+ , optOneline = False
, optNodef = "nodes"
, optInstf = "instances"
, optMaxRounds = -1
+ , optMaster = ""
}
{- | Start computing the solution at the given depth and recurse until
we find a valid solution or we exceed the maximum depth.
-}
-iterateDepth :: Cluster.Table
- -> Int -- ^ Current round
- -> Int -- ^ Max rounds
- -> IO Cluster.Table
-iterateDepth ini_tbl cur_round max_rounds =
- let Cluster.Table _ ini_il ini_cv ini_plc = ini_tbl
+iterateDepth :: Cluster.Table -- The starting table
+ -> Int -- ^ Current round
+ -> Int -- ^ Max rounds
+ -> Bool -- ^ Wheter to be silent
+ -> IO Cluster.Table -- The resulting table
+iterateDepth ini_tbl cur_round max_rounds oneline =
+ let Cluster.Table ini_nl ini_il ini_cv ini_plc = ini_tbl
all_inst = Container.elems ini_il
- fin_tbl = Cluster.checkMove ini_tbl all_inst
+ node_idx = Container.keys ini_nl
+ fin_tbl = Cluster.checkMove node_idx ini_tbl all_inst
(Cluster.Table _ _ fin_cv fin_plc) = fin_tbl
ini_plc_len = length ini_plc
fin_plc_len = length fin_plc
allowed_next = (max_rounds < 0 || cur_round < max_rounds)
in
do
- printf " - round %d: " cur_round
+ unless oneline $ printf " - round %d: " cur_round
hFlush stdout
let msg =
if fin_cv < ini_cv then
(fin_plc_len - ini_plc_len)
else
"no improvement, stopping\n"
- putStr msg
- hFlush stdout
+ unless oneline $ do
+ putStr msg
+ hFlush stdout
(if fin_cv < ini_cv then -- this round made success, try deeper
if allowed_next
- then iterateDepth fin_tbl (cur_round + 1) max_rounds
+ then iterateDepth fin_tbl (cur_round + 1) max_rounds oneline
-- don't go deeper, but return the better solution
else return fin_tbl
else
, Option ['C'] ["print-commands"]
(NoArg (\ opts -> opts { optShowCmds = True }))
"print the ganeti command list for reaching the solution"
+ , Option ['o'] ["oneline"]
+ (NoArg (\ opts -> opts { optOneline = True }))
+ "print the ganeti command list for reaching the solution"
, Option ['n'] ["nodes"]
(ReqArg (\ f opts -> opts { optNodef = f }) "FILE")
"the node list FILE"
, Option ['i'] ["instances"]
(ReqArg (\ f opts -> opts { optInstf = f }) "FILE")
"the instance list FILE"
+ , Option ['m'] ["master"]
+ (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
+ "collect data via RAPI at the given ADDRESS"
, Option ['r'] ["max-rounds"]
(ReqArg (\ i opts -> opts { optMaxRounds = (read i)::Int }) "N")
"do not run for more than R rounds(useful for very unbalanced clusters)"
return (foldl (flip id) defaultOptions o, n)
(_,_,errs) ->
ioError (userError (concat errs ++ usageInfo header options))
- where header = "Usage: hbal [OPTION...]"
+ where header = printf "hbal %s\nUsage: hbal [OPTION...]"
+ Version.version
-- | Main function.
main :: IO ()
main = do
cmd_args <- System.getArgs
(opts, _) <- parseOpts cmd_args
- (nl, il, ktn, kti) <- liftM2 Cluster.loadData
- (readFile $ optNodef opts)
- (readFile $ optInstf opts)
- printf "Loaded %d nodes, %d instances\n"
+
+ let oneline = optOneline opts
+ let (node_data, inst_data) =
+ case optMaster opts of
+ "" -> (readFile $ optNodef opts,
+ readFile $ optInstf opts)
+ host -> (readData getNodes host,
+ readData getInstances host)
+
+ (nl, il, csf, ktn, kti) <- liftM2 Cluster.loadData node_data inst_data
+
+ unless oneline $ printf "Loaded %d nodes, %d instances\n"
(Container.size nl)
(Container.size il)
+
+ when (length csf > 0 && not oneline) $ do
+ printf "Note: Stripping common suffix of '%s' from names\n" csf
+
let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
- printf "Initial check done: %d bad nodes, %d bad instances.\n"
+ unless oneline $ printf
+ "Initial check done: %d bad nodes, %d bad instances.\n"
(length bad_nodes) (length bad_instances)
when (length bad_nodes > 0) $ do
- putStrLn "Cluster is not N+1 happy, please fix N+1 first. Exiting."
- exitWith $ ExitFailure 1
+ putStrLn "Cluster is not N+1 happy, continuing but no guarantee \
+ \that the cluster will end N+1 happy."
when (optShowNodes opts) $
do
let ini_cv = Cluster.compCV nl
ini_tbl = Cluster.Table nl il ini_cv []
- printf "Initial coefficients: overall %.8f, %s\n"
- ini_cv (Cluster.printStats nl)
+ unless oneline $ printf "Initial coefficients: overall %.8f, %s\n"
+ ini_cv (Cluster.printStats nl)
- putStrLn "Trying to minimize the CV..."
- fin_tbl <- iterateDepth ini_tbl 1 (optMaxRounds opts)
+ unless oneline $ putStrLn "Trying to minimize the CV..."
+ fin_tbl <- iterateDepth ini_tbl 1 (optMaxRounds opts) oneline
let (Cluster.Table fin_nl _ fin_cv fin_plc) = fin_tbl
ord_plc = reverse fin_plc
- printf "Final coefficients: overall %.8f, %s\n"
+ unless oneline $ printf "Final coefficients: overall %.8f, %s\n"
fin_cv
(Cluster.printStats fin_nl)
- printf "Solution length=%d\n" (length ord_plc)
+ unless oneline $ printf "Solution length=%d\n" (length ord_plc)
let (sol_strs, cmd_strs) = Cluster.printSolution il ktn kti ord_plc
- putStr $ unlines $ sol_strs
+ unless oneline $ putStr $ unlines $ sol_strs
when (optShowCmds opts) $
do
putStrLn ""
putStrLn $ Cluster.printNodes ktn fin_nl
printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
printf "Final: mem=%d disk=%d\n" final_mem final_disk
+ when oneline $ do
+ printf "%.8f %d %.8f %8.3f\n"
+ ini_cv (length ord_plc) fin_cv (ini_cv / fin_cv)