commit 2922d2c576d85983eb5f6ac82982b641aacc954b
Author: René Nussbaumer <rn@google.com>
Date:   Mon Feb 27 10:33:08 2012 +0100

    hinfo: Adding basic skeleton based on hbal
    
    It prints the information which hbal prints in a nice formatted manner and of
    course without doing any actions. An example output looks like this:
    
    $ hinfo -m xen.example.com
    Loaded 4 nodes, 2 instances
    Cluster has 1 node group(s)
    Cluster coefficients:
       Field                Value Weight
       free_mem_cv     0.00195342  x1.00
       free_disk_cv    0.00000000  x1.00
       n1_cnt          0.00000000  x1.00
       reserved_mem_cv 0.00195342  x1.00
       offline_all_cnt 0.00000000  x4.00
       offline_pri_cnt 0.00000000 x16.00
       vcpu_ratio_cv   0.12500000  x1.00
       cpu_load_cv     0.50000000  x1.00
       mem_load_cv     0.50000000  x1.00
       disk_load_cv    0.00000000  x1.00
       net_load_cv     0.50000000  x1.00
       pri_tags_score  0.00000000  x2.00
    Cluster score: 1.62890685
    
    Signed-off-by: René Nussbaumer <rn@google.com>
    Reviewed-by: Iustin Pop <iustin@google.com>

diff --git a/Makefile.am b/Makefile.am
index 66f4e80..bcce5ea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -347,7 +347,7 @@ docrst = \
 	doc/walkthrough.rst
 
 HS_PROGS = htools/htools
-HS_BIN_ROLES = hbal hscan hspace
+HS_BIN_ROLES = hbal hscan hspace hinfo
 
 HS_ALL_PROGS = $(HS_PROGS) htools/test
 HS_PROG_SRCS = $(patsubst %,%.hs,$(HS_ALL_PROGS))
@@ -389,6 +389,7 @@ HS_LIB_SRCS = \
 	htools/Ganeti/HTools/Program.hs \
 	htools/Ganeti/HTools/Program/Hail.hs \
 	htools/Ganeti/HTools/Program/Hbal.hs \
+	htools/Ganeti/HTools/Program/Hinfo.hs \
 	htools/Ganeti/HTools/Program/Hscan.hs \
 	htools/Ganeti/HTools/Program/Hspace.hs \
 	htools/Ganeti/BasicTypes.hs \
diff --git a/htools/Ganeti/HTools/Cluster.hs b/htools/Ganeti/HTools/Cluster.hs
index 67170d1..37af920 100644
--- a/htools/Ganeti/HTools/Cluster.hs
+++ b/htools/Ganeti/HTools/Cluster.hs
@@ -1330,14 +1330,19 @@ printInsts nl il =
      formatTable (header:map helper sil) isnum
 
 -- | Shows statistics for a given node list.
-printStats :: Node.List -> String
-printStats nl =
+printStats :: String -> Node.List -> String
+printStats lp nl =
   let dcvs = compDetailedCV $ Container.elems nl
       (weights, names) = unzip detailedCVInfo
       hd = zip3 (weights ++ repeat 1) (names ++ repeat "unknown") dcvs
-      formatted = map (\(w, header, val) ->
-                         printf "%s=%.8f(x%.2f)" header val w::String) hd
-  in intercalate ", " formatted
+      header = [ "Field", "Value", "Weight" ]
+      formatted = map (\(w, h, val) ->
+                         [ h
+                         , printf "%.8f" val
+                         , printf "x%.2f" w
+                         ]) hd
+  in unlines . map ((++) lp) . map ((:) ' ' . unwords) $
+     formatTable (header:formatted) $ False:repeat True
 
 -- | Convert a placement into a list of OpCodes (basically a job).
 iMoveToJob :: Node.List        -- ^ The node list; only used for node
diff --git a/htools/Ganeti/HTools/Program.hs b/htools/Ganeti/HTools/Program.hs
index 3c7b825..2817e83 100644
--- a/htools/Ganeti/HTools/Program.hs
+++ b/htools/Ganeti/HTools/Program.hs
@@ -33,6 +33,7 @@ import qualified Ganeti.HTools.Program.Hail as Hail
 import qualified Ganeti.HTools.Program.Hbal as Hbal
 import qualified Ganeti.HTools.Program.Hscan as Hscan
 import qualified Ganeti.HTools.Program.Hspace as Hspace
+import qualified Ganeti.HTools.Program.Hinfo as Hinfo
 
 -- | Supported binaries.
 personalities :: [(String, (Options -> [String] -> IO (), [OptType]))]
@@ -40,4 +41,5 @@ personalities = [ ("hail",   (Hail.main,   Hail.options))
                 , ("hbal",   (Hbal.main,   Hbal.options))
                 , ("hscan",  (Hscan.main,  Hscan.options))
                 , ("hspace", (Hspace.main, Hspace.options))
+                , ("hinfo",  (Hinfo.main,  Hinfo.options))
                 ]
diff --git a/htools/Ganeti/HTools/Program/Hbal.hs b/htools/Ganeti/HTools/Program/Hbal.hs
index 30c3d61..b784833 100644
--- a/htools/Ganeti/HTools/Program/Hbal.hs
+++ b/htools/Ganeti/HTools/Program/Hbal.hs
@@ -373,8 +373,8 @@ main opts args = do
   checkNeedRebalance opts ini_cv
 
   if verbose > 2
-    then printf "Initial coefficients: overall %.8f, %s\n"
-           ini_cv (Cluster.printStats nl)::IO ()
+    then printf "Initial coefficients: overall %.8f\n%s"
+           ini_cv (Cluster.printStats "  " nl)::IO ()
     else printf "Initial score: %.8f\n" ini_cv
 
   putStrLn "Trying to minimize the CV..."
@@ -392,8 +392,8 @@ main opts args = do
       sol_msg = case () of
                   _ | null fin_plc -> printf "No solution found\n"
                     | verbose > 2 ->
-                        printf "Final coefficients:   overall %.8f, %s\n"
-                        fin_cv (Cluster.printStats fin_nl)
+                        printf "Final coefficients:   overall %.8f\n%s"
+                        fin_cv (Cluster.printStats "  " fin_nl)
                     | otherwise ->
                         printf "Cluster score improved from %.8f to %.8f\n"
                         ini_cv fin_cv ::String
diff --git a/htools/Ganeti/HTools/Program/Hinfo.hs b/htools/Ganeti/HTools/Program/Hinfo.hs
new file mode 100644
index 0000000..0f2502c
--- /dev/null
+++ b/htools/Ganeti/HTools/Program/Hinfo.hs
@@ -0,0 +1,111 @@
+{-| Cluster information printer.
+
+-}
+
+{-
+
+Copyright (C) 2012 Google Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+-}
+
+module Ganeti.HTools.Program.Hinfo (main, options) where
+
+import Control.Monad
+import Data.List
+import System.Exit
+import System.IO
+
+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 Ganeti.HTools.CLI
+import Ganeti.HTools.ExtLoader
+import Ganeti.HTools.Loader
+
+-- | Options list and functions.
+options :: [OptType]
+options =
+  [ oPrintNodes
+  , oPrintInsts
+  , oDataFile
+  , oRapiMaster
+  , oLuxiSocket
+  , oVerbose
+  , oQuiet
+  , oOfflineNode
+  , oShowVer
+  , oShowHelp
+  ]
+
+-- | Do a few checks on the cluster data.
+checkCluster :: Int -> Node.List -> Instance.List -> IO ()
+checkCluster verbose nl il = do
+  -- nothing to do on an empty cluster
+  when (Container.null il) $ do
+         printf "Cluster is empty, exiting.\n"::IO ()
+         exitWith ExitSuccess
+
+  -- hbal doesn't currently handle split clusters
+  let split_insts = Cluster.findSplitInstances nl il
+  unless (null split_insts) $ do
+    hPutStrLn stderr "Found instances belonging to multiple node groups:"
+    mapM_ (\i -> hPutStrLn stderr $ "  " ++ Instance.name i) split_insts
+    hPutStrLn stderr "Aborting."
+    exitWith $ ExitFailure 1
+
+  printf "Loaded %d nodes, %d instances\n"
+             (Container.size nl)
+             (Container.size il)::IO ()
+
+  let csf = commonSuffix nl il
+  when (not (null csf) && verbose > 1) $
+       printf "Note: Stripping common suffix of '%s' from names\n" csf
+
+-- | Main function.
+main :: Options -> [String] -> IO ()
+main opts args = do
+  unless (null args) $ do
+         hPutStrLn stderr "Error: this program doesn't take any arguments."
+         exitWith $ ExitFailure 1
+
+  let verbose = optVerbose opts
+      shownodes = optShowNodes opts
+      showinsts = optShowInsts opts
+
+  (ClusterData gl fixed_nl ilf ctags ipol) <- loadExternalData opts
+
+  when (verbose > 1) $ do
+       putStrLn $ "Loaded cluster tags: " ++ intercalate "," ctags
+       putStrLn $ "Loaded cluster ipolicy: " ++ show ipol
+       putStrLn $ "Loaded node groups: " ++ show gl
+
+  nlf <- setNodeStatus opts fixed_nl
+  checkCluster verbose nlf ilf
+
+  printf "Cluster has %d node group(s)\n" (Container.size gl)::IO ()
+
+  maybePrintInsts showinsts "Instances" (Cluster.printInsts nlf ilf)
+
+  maybePrintNodes shownodes "Cluster" (Cluster.printNodes nlf)
+
+  printf "Cluster coefficients:\n%s" (Cluster.printStats "  " nlf)::IO ()
+  printf "Cluster score: %.8f\n" (Cluster.compCV nlf)
diff --git a/htools/Ganeti/HTools/Program/Hspace.hs b/htools/Ganeti/HTools/Program/Hspace.hs
index 6b8d11a..3b64159 100644
--- a/htools/Ganeti/HTools/Program/Hspace.hs
+++ b/htools/Ganeti/HTools/Program/Hspace.hs
@@ -424,8 +424,8 @@ main opts args = do
   maybePrintNodes (optShowNodes opts) "Initial cluster" (Cluster.printNodes nl)
 
   when (verbose > 2) $
-         hPrintf stderr "Initial coefficients: overall %.8f, %s\n"
-                 (Cluster.compCV nl) (Cluster.printStats nl)
+         hPrintf stderr "Initial coefficients: overall %.8f\n%s"
+                 (Cluster.compCV nl) (Cluster.printStats "  " nl)
 
   printCluster machine_r (Cluster.totalResources nl) (length all_nodes)
 
