Skip to content
Snippets Groups Projects
hn1.hs 6.89 KiB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
{-| 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
Iustin Pop's avatar
Iustin Pop committed
import Ganeti.HTools.Types
Iustin Pop's avatar
Iustin Pop committed

-- | 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
Iustin Pop's avatar
Iustin Pop committed
    , optMinDepth    :: Int
    , optMaxRemovals :: Int
    , optMinDelta    :: Int
    , optMaxDelta    :: Int
Iustin Pop's avatar
Iustin Pop committed
    , optMaster      :: String
    , optShowVer     :: Bool     -- ^ Just show the program version
    , optShowHelp    :: Bool     -- ^ Just show the help
Iustin Pop's avatar
Iustin Pop committed
    } deriving Show

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

Iustin Pop's avatar
Iustin Pop committed
-- | Default values for the command line options.
defaultOptions :: Options
defaultOptions    = Options
 { optShowNodes   = False
 , optShowCmds    = False
 , optNodef       = "nodes"
 , optNodeSet     = False
Iustin Pop's avatar
Iustin Pop committed
 , optInstf       = "instances"
 , optInstSet     = False
Iustin Pop's avatar
Iustin Pop committed
 , optMinDepth    = 1
 , optMaxRemovals = -1
 , optMinDelta    = 0
 , optMaxDelta    = -1
 , optMaster      = ""
 , optShowVer     = False
 , optShowHelp    = False
Iustin Pop's avatar
Iustin Pop committed
 }

{- | Start computing the solution at the given depth and recurse until
we find a valid solution or we exceed the maximum depth.

-}
Iustin Pop's avatar
Iustin Pop committed
iterateDepth :: NodeList
Iustin Pop's avatar
Iustin Pop committed
             -> [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"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['n']     ["nodes"]
      (ReqArg (\ f opts -> opts { optNodef = f, optNodeSet = True }) "FILE")
Iustin Pop's avatar
Iustin Pop committed
      "the node list FILE"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['i']     ["instances"]
      (ReqArg (\ f opts -> opts { optInstf =  f, optInstSet = True }) "FILE")
Iustin Pop's avatar
Iustin Pop committed
      "the instance list FILE"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['d']     ["depth"]
Iustin Pop's avatar
Iustin Pop committed
      (ReqArg (\ i opts -> opts { optMinDepth =  (read i)::Int }) "D")
      "start computing the solution at depth D"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['r']     ["max-removals"]
Iustin Pop's avatar
Iustin Pop committed
      (ReqArg (\ i opts -> opts { optMaxRemovals =  (read i)::Int }) "R")
      "do not process more than R removal sets (useful for high depths)"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['L']     ["max-delta"]
Iustin Pop's avatar
Iustin Pop committed
      (ReqArg (\ i opts -> opts { optMaxDelta =  (read i)::Int }) "L")
      "refuse solutions with delta higher than L"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['l']     ["min-delta"]
Iustin Pop's avatar
Iustin Pop committed
      (ReqArg (\ i opts -> opts { optMinDelta =  (read i)::Int }) "L")
      "return once a solution with delta L or lower has been found"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['m']     ["master"]
Iustin Pop's avatar
Iustin Pop committed
      (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
      "collect data via RAPI at the given ADDRESS"
Iustin Pop's avatar
Iustin Pop committed
    , Option ['V']     ["version"]
      (NoArg (\ opts -> opts { optShowVer = True}))
      "show the version of the program"
    , Option ['h']     ["help"]
      (NoArg (\ opts -> opts { optShowHelp = True}))
      "show help"
Iustin Pop's avatar
Iustin Pop committed
    ]
Iustin Pop's avatar
Iustin Pop committed

-- | Main function.
main :: IO ()
main = do
  cmd_args <- System.getArgs
  (opts, args) <- CLI.parseOpts cmd_args "hn1" options
                  defaultOptions

  unless (null args) $ do
         hPutStrLn stderr "Error: this program doesn't take any arguments."
         exitWith $ ExitFailure 1
Iustin Pop's avatar
Iustin Pop committed

  (nl, il, csf) <- CLI.loadExternalData opts
Iustin Pop's avatar
Iustin Pop committed
  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

Iustin Pop's avatar
Iustin Pop committed
  let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
      min_depth = optMinDepth opts

Iustin Pop's avatar
Iustin Pop committed
  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)

Iustin Pop's avatar
Iustin Pop committed
  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)
Iustin Pop's avatar
Iustin Pop committed
            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
Iustin Pop's avatar
Iustin Pop committed
  let (sol_strs, cmd_strs) = Cluster.printSolution ns il solution
Iustin Pop's avatar
Iustin Pop committed
  putStr $ unlines $ sol_strs
  when (optShowCmds opts) $
       do
         putStrLn ""
         putStrLn "Commands to run to reach the above solution:"
         putStr . Cluster.formatCmds . reverse $ cmd_strs

Iustin Pop's avatar
Iustin Pop committed
  when (optShowNodes opts) $
       do
         let (orig_mem, orig_disk) = Cluster.totalResources nl
             (final_mem, final_disk) = Cluster.totalResources ns
         putStrLn ""
         putStrLn "Final cluster status:"
         putStrLn $ Cluster.printNodes ns
Iustin Pop's avatar
Iustin Pop committed
         printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
         printf "Final:    mem=%d disk=%d\n" final_mem final_disk