Newer
Older
{-| Solver for N+1 cluster errors
-}
module Main (main) where
import Data.List
import Data.Function
import Monad
import System
import System.IO
import System.Console.GetOpt
import qualified System
import Text.Printf (printf)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Cluster as Cluster
import qualified Ganeti.HTools.CLI as CLI
-- | Command line options structure.
data Options = Options
{ optShowNodes :: Bool
, optShowCmds :: Bool
, optNodef :: FilePath
, optNodeSet :: Bool -- ^ The nodes have been set by options
, optInstf :: FilePath -- ^ Path to the instances file
, optInstSet :: Bool -- ^ The insts have been set by options
, optMinDepth :: Int
, optMaxRemovals :: Int
, optMinDelta :: Int
, optMaxDelta :: Int
, optMaster :: String
, optShowVer :: Bool -- ^ Just show the program version
, optShowHelp :: Bool -- ^ Just show the help
instance CLI.CLIOptions Options where
showVersion = optShowVer
showHelp = optShowHelp
instance CLI.EToolOptions Options where
nodeFile = optNodef
nodeSet = optNodeSet
instFile = optInstf
instSet = optInstSet
masterName = optMaster
silent _ = False
-- | Default values for the command line options.
defaultOptions :: Options
defaultOptions = Options
{ optShowNodes = False
, optShowCmds = False
, optNodef = "nodes"
, optMinDepth = 1
, optMaxRemovals = -1
, optMinDelta = 0
, optMaxDelta = -1
, optMaster = ""
, optShowVer = False
, optShowHelp = False
}
{- | Start computing the solution at the given depth and recurse until
we find a valid solution or we exceed the maximum depth.
-}
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
-> [Instance.Instance]
-> Int
-> Int
-> Int
-> Int
-> IO (Maybe Cluster.Solution)
iterateDepth nl bad_instances depth max_removals min_delta max_delta =
let
max_depth = length bad_instances
sol = Cluster.computeSolution nl bad_instances depth
max_removals min_delta max_delta
in
do
printf "%d " depth
hFlush stdout
case sol `seq` sol of
Nothing ->
if depth > max_depth then
return Nothing
else
iterateDepth nl bad_instances (depth + 1)
max_removals min_delta max_delta
_ -> return sol
-- | Options list and functions
options :: [OptDescr (Options -> Options)]
options =
[ Option ['p'] ["print-nodes"]
(NoArg (\ opts -> opts { optShowNodes = True }))
"print the final node list"
, Option ['C'] ["print-commands"]
(NoArg (\ opts -> opts { optShowCmds = True }))
"print the ganeti command list for reaching the solution"
(ReqArg (\ f opts -> opts { optNodef = f, optNodeSet = True }) "FILE")
(ReqArg (\ f opts -> opts { optInstf = f, optInstSet = True }) "FILE")
(ReqArg (\ i opts -> opts { optMinDepth = (read i)::Int }) "D")
"start computing the solution at depth D"
(ReqArg (\ i opts -> opts { optMaxRemovals = (read i)::Int }) "R")
"do not process more than R removal sets (useful for high depths)"
(ReqArg (\ i opts -> opts { optMaxDelta = (read i)::Int }) "L")
"refuse solutions with delta higher than L"
(ReqArg (\ i opts -> opts { optMinDelta = (read i)::Int }) "L")
"return once a solution with delta L or lower has been found"
(ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
"collect data via RAPI at the given ADDRESS"
, Option ['V'] ["version"]
(NoArg (\ opts -> opts { optShowVer = True}))
"show the version of the program"
, Option ['h'] ["help"]
(NoArg (\ opts -> opts { optShowHelp = True}))
"show help"
-- | Main function.
main :: IO ()
main = do
cmd_args <- System.getArgs
(opts, args) <- CLI.parseOpts cmd_args "hn1" options
unless (null args) $ do
hPutStrLn stderr "Error: this program doesn't take any arguments."
exitWith $ ExitFailure 1
(nl, il, csf) <- CLI.loadExternalData opts
printf "Loaded %d nodes, %d instances\n"
(Container.size nl)
(Container.size il)
when (length csf > 0) $ 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"
(length bad_nodes) (length bad_instances)
when (null bad_instances) $ do
putStrLn "Happy time! Cluster is fine, no need to burn CPU."
exitWith ExitSuccess
when (length bad_instances < min_depth) $ do
printf "Error: depth %d is higher than the number of bad instances.\n"
min_depth
exitWith $ ExitFailure 2
let ini_cv = Cluster.compCV nl
printf "Initial coefficients: overall %.8f, %s\n"
ini_cv (Cluster.printStats nl)
putStr "Computing solution: depth "
hFlush stdout
result <- iterateDepth nl bad_instances min_depth (optMaxRemovals opts)
(optMinDelta opts) (optMaxDelta opts)
let (min_d, solution) =
case result of
Just (Cluster.Solution a b) -> (a, reverse b)
Nothing -> (-1, [])
when (min_d == -1) $ do
putStrLn "failed. Try to run with higher depth."
exitWith $ ExitFailure 1
printf "found.\n"
let
ns = Cluster.applySolution nl il solution
fin_cv = Cluster.compCV ns
printf "Final coefficients: overall %.8f, %s\n"
fin_cv
(Cluster.printStats ns)
printf "Solution (delta=%d):\n" $! min_d
let (sol_strs, cmd_strs) = Cluster.printSolution ns il solution
putStr $ unlines $ sol_strs
when (optShowCmds opts) $
do
putStrLn ""
putStrLn "Commands to run to reach the above solution:"
putStr . Cluster.formatCmds . reverse $ cmd_strs
when (optShowNodes opts) $
do
let (orig_mem, orig_disk) = Cluster.totalResources nl
(final_mem, final_disk) = Cluster.totalResources ns
putStrLn ""
putStrLn "Final cluster status:"
printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
printf "Final: mem=%d disk=%d\n" final_mem final_disk