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.Cluster as Cluster
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.CLI as CLI
import Ganeti.HTools.IAlloc
-- | Command line options structure.
data Options = Options
{ optShowVer :: Bool -- ^ Just show the program version
, optShowHelp :: Bool -- ^ Just show the help
} deriving Show
-- | Default values for the command line options.
defaultOptions :: Options
defaultOptions = Options
instance CLI.CLIOptions Options where
showVersion = optShowVer
showHelp = optShowHelp
-- | Options list and functions
options :: [OptDescr (Options -> Options)]
options =
(NoArg (\ opts -> opts { optShowVer = True}))
"show the version of the program"
, Option ['h'] ["help"]
(NoArg (\ opts -> opts { optShowHelp = True}))
"show help"
]
-- | Compute online nodes from a NodeList
getOnline :: NodeList -> [Node.Node]
getOnline = filter (not . Node.offline) . Container.elems
-- | Try to allocate an instance on the cluster
tryAlloc :: (Monad m) =>
NodeList
-> InstanceList
-> Instance.Instance
-> Int
-> m [(Maybe NodeList, [Node.Node])]
all_pairs = liftM2 (,) all_nodes all_nodes
ok_pairs = filter (\(x, y) -> Node.idx x /= Node.idx y) all_pairs
sols = map (\(p, s) ->
(fst $ Cluster.allocateOnPair nl inst p s, [p, s]))
ok_pairs
in return sols
tryAlloc nl _ inst 1 =
sols = map (\p -> (fst $ Cluster.allocateOnSingle nl inst p, [p]))
all_nodes
in return sols
tryAlloc _ _ _ reqn = fail $ "Unsupported number of alllocation \
\destinations required (" ++ (show reqn) ++
"), only two supported"
-- | Try to allocate an instance on the cluster
tryReloc :: (Monad m) =>
NodeList
-> InstanceList
-> Int
-> Int
-> [Int]
-> m [(Maybe NodeList, [Node.Node])]
valid_nodes = filter (not . flip elem ex_idx . idx) all_nodes
valid_idxes = map Node.idx valid_nodes
nl' = Container.map (\n -> if elem (Node.idx n) ex_idx then
Node.setOffline n True
else n) nl
sols1 = map (\x -> let (mnl, _, _, _) =
Cluster.applyMove nl' inst
(Cluster.ReplaceSecondary x)
in (mnl, [Container.find x nl'])
in return sols1
tryReloc _ _ _ reqn _ = fail $ "Unsupported number of relocation \
\destinations required (" ++ (show reqn) ++
"), only one supported"
filterFails :: (Monad m) => [(Maybe NodeList, [Node.Node])]
-> m [(NodeList, [Node.Node])]
filterFails sols =
if null sols then fail "No nodes onto which to allocate at all"
else let sols' = filter (isJust . fst) sols
in if null sols' then
fail "No valid allocation solutions"
return $ map (\(x, y) -> (fromJust x, y)) sols'
processResults :: (Monad m) => [(NodeList, [Node.Node])]
-> m (String, [Node.Node])
processResults sols =
let sols' = map (\(nl', ns) -> (Cluster.compCV nl', ns)) sols
sols'' = sortBy (compare `on` fst) sols'
(best, w) = head sols''
(worst, l) = last sols''
info = printf "Valid results: %d, best score: %.8f for node(s) %s, \
\worst score: %.8f for node(s) %s" (length sols'')
best (intercalate "/" . map Node.name $ w)
worst (intercalate "/" . map Node.name $ l)
in return (info, w)
-- | Main function.
main :: IO ()
main = do
cmd_args <- System.getArgs
(_, args) <- CLI.parseOpts cmd_args "hail" options defaultOptions
when (null args) $ do
hPutStrLn stderr "Error: this program needs an input file."
exitWith $ ExitFailure 1
let input_file = head args
input_data <- readFile input_file
request <- case (parseData input_data) of
Bad err -> do
putStrLn $ "Error: " ++ err
exitWith $ ExitFailure 1
Ok rq -> return rq
let Request rqtype nl il csf = request
new_nodes = case rqtype of
Allocate xi reqn -> tryAlloc nl il xi reqn
Relocate idx reqn exnodes ->
tryReloc nl il idx reqn exnodes
let sols = new_nodes >>= filterFails >>= processResults
let (ok, info, rn) = case sols of
Ok (info, sn) -> (True, "Request successful: " ++ info,
Bad s -> (False, "Request failed: " ++ s, [])
resp = formatResponse ok info rn
putStrLn resp