From 1b7a58350c56a6fdf13f69ead4b5684647332bee Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Sat, 21 Mar 2009 23:50:47 +0100
Subject: [PATCH] Add the hscan tool

This patch adds an hscan tool that loads data from clusters via RAPI and
writes it to files that can be later used offline.
---
 hscan.hs | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 hscan.hs

diff --git a/hscan.hs b/hscan.hs
new file mode 100644
index 000000000..4272b795a
--- /dev/null
+++ b/hscan.hs
@@ -0,0 +1,178 @@
+{-| Scan clusters via RAPI and write instance/node data files.
+
+-}
+
+module Main (main) where
+
+import Data.List
+import Data.Function
+import Data.Maybe(fromJust)
+import Monad
+import System
+import System.IO
+import System.FilePath
+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.Version as Version
+import qualified Ganeti.HTools.Node as Node
+import qualified Ganeti.HTools.Instance as Instance
+import Ganeti.HTools.Rapi
+import Ganeti.HTools.Utils
+
+-- | Command line options structure.
+data Options = Options
+    { optShowNodes :: Bool     -- ^ Whether to show node status
+    , optOutPath   :: FilePath -- ^ Path to the output directory
+    , optVerbose   :: Int      -- ^ Verbosity level
+    , optShowVer   :: Bool     -- ^ Just show the program version
+    , optNoHeader  :: Bool     -- ^ Do not show a header line
+    } deriving Show
+
+-- | Default values for the command line options.
+defaultOptions :: Options
+defaultOptions  = Options
+ { optShowNodes = False
+ , optOutPath   = "."
+ , optVerbose   = 0
+ , optShowVer   = False
+ , optNoHeader  = False
+ }
+
+-- | Options list and functions
+options :: [OptDescr (Options -> Options)]
+options =
+    [ Option ['p']     ["print-nodes"]
+      (NoArg (\ opts -> opts { optShowNodes = True }))
+      "print the final node list"
+    , Option ['d']     ["output-dir"]
+      (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
+      "directory in which to write output files"
+    , Option ['v']     ["verbose"]
+      (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
+      "increase the verbosity level"
+    , Option ['V']     ["version"]
+      (NoArg (\ opts -> opts { optShowVer = True}))
+      "show the version of the program"
+    , Option []        ["no-headers"]
+      (NoArg (\ opts -> opts { optNoHeader = True }))
+      "do not show a header line"
+    ]
+
+-- | Command line parser, using the 'options' structure.
+parseOpts :: [String] -> IO (Options, [String])
+parseOpts argv =
+    case getOpt Permute options argv of
+      (o, n, []) ->
+          return (foldl (flip id) defaultOptions o, n)
+      (_, _, errs) ->
+          ioError (userError (concat errs ++ usageInfo header options))
+      where header = printf "hscan %s\nUsage: hscan [OPTION...] cluster..."
+                     Version.version
+
+-- | Generate node file data from node objects
+serializeNodes :: Cluster.NodeList -> String -> Cluster.NameList -> String
+serializeNodes nl csf ktn =
+    let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
+        nodes = Container.elems nl
+        nlines = map
+                 (\node ->
+                      let name = (fromJust $ lookup (Node.idx node) etn)
+                          t_mem = (truncate $ Node.t_mem node)::Integer
+                          t_dsk = (truncate $ Node.t_dsk node)::Integer
+                      in
+                        printf "%s|%d|%d|%d|%d" name
+                                   t_mem (Node.f_mem node)
+                                   t_dsk (Node.f_dsk node))
+                 nodes
+    in unlines nlines
+
+-- | Generate instance file data from instance objects
+serializeInstances :: Cluster.InstanceList -> String
+                   -> Cluster.NameList -> Cluster.NameList -> String
+serializeInstances il csf ktn kti =
+    let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
+        eti = map (\(idx, name) -> (idx, name ++ csf)) kti
+        instances = Container.elems il
+        nlines = map
+                 (\inst ->
+                      let
+                          iname = fromJust $ lookup (Instance.idx inst) eti
+                          pnode = fromJust $ lookup (Instance.pnode inst) etn
+                          snode = fromJust $ lookup (Instance.snode inst) etn
+                      in
+                        printf "%s|%d|%d|%s|%s"
+                               iname (Instance.mem inst) (Instance.dsk inst)
+                               pnode snode
+                 )
+                 instances
+    in unlines nlines
+
+-- | Return a one-line summary of cluster state
+printCluster :: Cluster.NodeList -> Cluster.InstanceList
+             -> Cluster.NameList -> Cluster.NameList
+             -> String
+printCluster nl il ktn kti =
+    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
+        ccv = Cluster.compCV nl
+        nodes = Container.elems nl
+        t_ram = truncate . sum . map Node.t_mem $ nodes
+        t_dsk = truncate . sum . map Node.t_dsk $ nodes
+        f_ram = sum . map Node.f_mem $ nodes
+        f_dsk = sum . map Node.f_dsk $ nodes
+    in
+      printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
+                 (length ktn) (length kti)
+                 (length bad_nodes) (length bad_instances)
+                 (t_ram::Integer) f_ram
+                 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
+                 ccv
+
+
+-- | Main function.
+main :: IO ()
+main = do
+  cmd_args <- System.getArgs
+  (opts, clusters) <- parseOpts cmd_args
+
+  when (optShowVer opts) $ do
+         putStr $ showVersion "hscan"
+         exitWith ExitSuccess
+
+  let odir = optOutPath opts
+      nlen = maximum . map length $ clusters
+
+  unless (optNoHeader opts) $
+         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
+                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
+                "t_disk" "f_disk" "Score"
+
+  mapM (\ name ->
+            do
+              printf "%-*s " nlen name
+              hFlush stdout
+              node_data <- getNodes name
+              inst_data <- getInstances name
+              (if isLeft(node_data)
+               then putStrLn $ fromLeft node_data
+               else if isLeft(inst_data)
+                    then putStrLn $ fromLeft inst_data
+                    else do
+                      let ndata = fromRight node_data
+                          idata = fromRight inst_data
+                          (nl, il, csf, ktn, kti) =
+                              Cluster.loadData ndata idata
+                      putStrLn $ printCluster nl il ktn kti
+                      when (optShowNodes opts) $
+                           putStr $ Cluster.printNodes ktn nl
+                      let ndata = serializeNodes nl csf ktn
+                          idata = serializeInstances il csf ktn kti
+                          oname = odir </> name
+                      writeFile (oname <.> "nodes") ndata
+                      writeFile (oname <.> "instances") idata)
+       ) clusters
+  exitWith ExitSuccess
-- 
GitLab