First try to embed VCS id in binaries
[ganeti-local] / src / hbal.hs
index c5089db..7b98d19 100644 (file)
@@ -16,14 +16,19 @@ import Text.Printf (printf)
 
 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.
@@ -31,30 +36,34 @@ 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 =
-    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
@@ -67,11 +76,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
@@ -86,12 +96,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)"
@@ -105,26 +121,40 @@ parseOpts argv =
           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
@@ -133,21 +163,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 ""
@@ -162,3 +192,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)