Implement oneline-output for hbal
[ganeti-local] / src / hbal.hs
index 5a9eef7..58a3008 100644 (file)
@@ -17,14 +17,17 @@ import Text.Printf (printf)
 import qualified Container
 import qualified Cluster
 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.
@@ -32,20 +35,23 @@ defaultOptions :: 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 =
+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_il ini_cv ini_plc = ini_tbl
         all_inst = Container.elems ini_il
         fin_tbl = Cluster.checkMove ini_tbl all_inst
@@ -55,7 +61,7 @@ iterateDepth ini_tbl cur_round max_rounds =
         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
@@ -68,11 +74,12 @@ iterateDepth ini_tbl cur_round max_rounds =
                                    (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
@@ -87,12 +94,18 @@ options =
     , 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)"
@@ -108,23 +121,39 @@ parseOpts argv =
           ioError (userError (concat errs ++ usageInfo header options))
       where header = "Usage: hbal [OPTION...]"
 
+-- | Get a Right result or print the error and exit
+readData :: (String -> IO (Either String String)) -> String -> IO String
+readData fn host = do
+  nd <- fn host
+  when (isLeft nd) $
+       do
+         putStrLn $ fromLeft nd
+         exitWith $ ExitFailure 1
+  return $ fromRight nd
+
 -- | Main function.
 main :: IO ()
 main = do
-  i <- getInstances "gnta1"
-  n <- getNodes "gnta1"
-  print i
-  print n
   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, ktn, kti) <- liftM2 Cluster.loadData node_data inst_data
+
+
+  unless oneline $ printf "Loaded %d nodes, %d instances\n"
              (Container.size nl)
              (Container.size il)
   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
@@ -138,21 +167,21 @@ main = 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 ""
@@ -167,3 +196,6 @@ main = do
          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)