1 {-| Scan clusters via RAPI and write instance/node data files.
5 module Main (main) where
9 import Data.Maybe(fromJust)
13 import System.FilePath
14 import System.Console.GetOpt
15 import qualified System
17 import Text.Printf (printf)
19 import qualified Ganeti.HTools.Container as Container
20 import qualified Ganeti.HTools.Cluster as Cluster
21 import qualified Ganeti.HTools.Version as Version
22 import qualified Ganeti.HTools.Node as Node
23 import qualified Ganeti.HTools.Instance as Instance
24 import Ganeti.HTools.Rapi
25 import Ganeti.HTools.Utils
27 -- | Command line options structure.
28 data Options = Options
29 { optShowNodes :: Bool -- ^ Whether to show node status
30 , optOutPath :: FilePath -- ^ Path to the output directory
31 , optVerbose :: Int -- ^ Verbosity level
32 , optShowVer :: Bool -- ^ Just show the program version
33 , optNoHeader :: Bool -- ^ Do not show a header line
36 -- | Default values for the command line options.
37 defaultOptions :: Options
38 defaultOptions = Options
39 { optShowNodes = False
46 -- | Options list and functions
47 options :: [OptDescr (Options -> Options)]
49 [ Option ['p'] ["print-nodes"]
50 (NoArg (\ opts -> opts { optShowNodes = True }))
51 "print the final node list"
52 , Option ['d'] ["output-dir"]
53 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
54 "directory in which to write output files"
55 , Option ['v'] ["verbose"]
56 (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
57 "increase the verbosity level"
58 , Option ['V'] ["version"]
59 (NoArg (\ opts -> opts { optShowVer = True}))
60 "show the version of the program"
61 , Option [] ["no-headers"]
62 (NoArg (\ opts -> opts { optNoHeader = True }))
63 "do not show a header line"
66 -- | Command line parser, using the 'options' structure.
67 parseOpts :: [String] -> IO (Options, [String])
69 case getOpt Permute options argv of
71 return (foldl (flip id) defaultOptions o, n)
73 ioError (userError (concat errs ++ usageInfo header options))
74 where header = printf "hscan %s\nUsage: hscan [OPTION...] cluster..."
77 -- | Generate node file data from node objects
78 serializeNodes :: Cluster.NodeList -> String -> Cluster.NameList -> String
79 serializeNodes nl csf ktn =
80 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
81 nodes = Container.elems nl
84 let name = (fromJust $ lookup (Node.idx node) etn)
85 t_mem = (truncate $ Node.t_mem node)::Integer
86 t_dsk = (truncate $ Node.t_dsk node)::Integer
88 printf "%s|%d|%d|%d|%d" name
89 t_mem (Node.f_mem node)
90 t_dsk (Node.f_dsk node))
94 -- | Generate instance file data from instance objects
95 serializeInstances :: Cluster.InstanceList -> String
96 -> Cluster.NameList -> Cluster.NameList -> String
97 serializeInstances il csf ktn kti =
98 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
99 eti = map (\(idx, name) -> (idx, name ++ csf)) kti
100 instances = Container.elems il
104 iname = fromJust $ lookup (Instance.idx inst) eti
105 pnode = fromJust $ lookup (Instance.pnode inst) etn
106 snode = fromJust $ lookup (Instance.snode inst) etn
108 printf "%s|%d|%d|%s|%s"
109 iname (Instance.mem inst) (Instance.dsk inst)
115 -- | Return a one-line summary of cluster state
116 printCluster :: Cluster.NodeList -> Cluster.InstanceList
117 -> Cluster.NameList -> Cluster.NameList
119 printCluster nl il ktn kti =
120 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
121 ccv = Cluster.compCV nl
122 nodes = Container.elems nl
123 t_ram = truncate . sum . map Node.t_mem $ nodes
124 t_dsk = truncate . sum . map Node.t_dsk $ nodes
125 f_ram = sum . map Node.f_mem $ nodes
126 f_dsk = sum . map Node.f_dsk $ nodes
128 printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
129 (length ktn) (length kti)
130 (length bad_nodes) (length bad_instances)
131 (t_ram::Integer) f_ram
132 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
139 cmd_args <- System.getArgs
140 (opts, clusters) <- parseOpts cmd_args
142 when (optShowVer opts) $ do
143 putStr $ showVersion "hscan"
146 let odir = optOutPath opts
147 nlen = maximum . map length $ clusters
149 unless (optNoHeader opts) $
150 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
151 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
152 "t_disk" "f_disk" "Score"
156 printf "%-*s " nlen name
158 node_data <- getNodes name
159 inst_data <- getInstances name
160 (if isLeft(node_data)
161 then putStrLn $ fromLeft node_data
162 else if isLeft(inst_data)
163 then putStrLn $ fromLeft inst_data
165 let ndata = fromRight node_data
166 idata = fromRight inst_data
167 (nl, il, csf, ktn, kti) =
168 Cluster.loadData ndata idata
169 putStrLn $ printCluster nl il ktn kti
170 when (optShowNodes opts) $
171 putStr $ Cluster.printNodes ktn nl
172 let ndata = serializeNodes nl csf ktn
173 idata = serializeInstances il csf ktn kti
174 oname = odir </> name
175 writeFile (oname <.> "nodes") ndata
176 writeFile (oname <.> "instances") idata)