1 {-| Scan clusters via RAPI and write instance/node data files.
7 Copyright (C) 2009 Google Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 module Main (main) where
33 import System.FilePath
34 import System.Console.GetOpt
35 import qualified System
37 import Text.Printf (printf)
39 import qualified Ganeti.HTools.Container as Container
40 import qualified Ganeti.HTools.Cluster as Cluster
41 import qualified Ganeti.HTools.Node as Node
42 import qualified Ganeti.HTools.Instance as Instance
43 import qualified Ganeti.HTools.CLI as CLI
44 import qualified Ganeti.HTools.Rapi as Rapi
45 import qualified Ganeti.HTools.Loader as Loader
46 import Ganeti.HTools.Types
48 -- | Command line options structure.
49 data Options = Options
50 { optShowNodes :: Bool -- ^ Whether to show node status
51 , optOutPath :: FilePath -- ^ Path to the output directory
52 , optVerbose :: Int -- ^ Verbosity level
53 , optNoHeader :: Bool -- ^ Do not show a header line
54 , optShowVer :: Bool -- ^ Just show the program version
55 , optShowHelp :: Bool -- ^ Just show the help
58 instance CLI.CLIOptions Options where
59 showVersion = optShowVer
60 showHelp = optShowHelp
62 -- | Default values for the command line options.
63 defaultOptions :: Options
64 defaultOptions = Options
65 { optShowNodes = False
73 -- | Options list and functions
74 options :: [OptDescr (Options -> Options)]
76 [ Option ['p'] ["print-nodes"]
77 (NoArg (\ opts -> opts { optShowNodes = True }))
78 "print the final node list"
79 , Option ['d'] ["output-dir"]
80 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
81 "directory in which to write output files"
82 , Option ['v'] ["verbose"]
83 (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
84 "increase the verbosity level"
85 , Option [] ["no-headers"]
86 (NoArg (\ opts -> opts { optNoHeader = True }))
87 "do not show a header line"
88 , Option ['V'] ["version"]
89 (NoArg (\ opts -> opts { optShowVer = True}))
90 "show the version of the program"
91 , Option ['h'] ["help"]
92 (NoArg (\ opts -> opts { optShowHelp = True}))
96 -- | Serialize a single node
97 serializeNode :: String -> Node.Node -> String
98 serializeNode csf node =
99 printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c" (Node.name node ++ csf)
100 (Node.t_mem node) (Node.n_mem node) (Node.f_mem node)
101 (Node.t_dsk node) (Node.f_dsk node) (Node.t_cpu node)
102 (if Node.offline node then 'Y' else 'N')
104 -- | Generate node file data from node objects
105 serializeNodes :: String -> Node.List -> String
107 unlines . map (serializeNode csf) . Container.elems
109 -- | Serialize a single instance
110 serializeInstance :: String -> Node.List -> Instance.Instance -> String
111 serializeInstance csf nl inst =
113 iname = Instance.name inst ++ csf
114 pnode = Container.nameOf nl (Instance.pnode inst) ++ csf
115 sidx = Instance.snode inst
116 snode = (if sidx == Node.noSecondary
118 else Container.nameOf nl sidx ++ csf)
120 printf "%s|%d|%d|%d|%s|%s|%s"
121 iname (Instance.mem inst) (Instance.dsk inst)
122 (Instance.vcpus inst) (Instance.run_st inst)
125 -- | Generate instance file data from instance objects
126 serializeInstances :: String -> Node.List -> Instance.List -> String
127 serializeInstances csf nl =
128 unlines . map (serializeInstance csf nl) . Container.elems
130 -- | Return a one-line summary of cluster state
131 printCluster :: Node.List -> Instance.List
134 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
135 ccv = Cluster.compCV nl
136 nodes = Container.elems nl
137 insts = Container.elems il
138 t_ram = sum . map Node.t_mem $ nodes
139 t_dsk = sum . map Node.t_dsk $ nodes
140 f_ram = sum . map Node.f_mem $ nodes
141 f_dsk = sum . map Node.f_dsk $ nodes
143 printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
144 (length nodes) (length insts)
145 (length bad_nodes) (length bad_instances)
147 (t_dsk / 1024) (f_dsk `div` 1024)
151 -- | Replace slashes with underscore for saving to filesystem
153 fixSlash :: String -> String
154 fixSlash = map (\x -> if x == '/' then '_' else x)
159 cmd_args <- System.getArgs
160 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
163 let odir = optOutPath opts
164 nlen = maximum . map length $ clusters
166 unless (optNoHeader opts) $
167 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
168 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
169 "t_disk" "f_disk" "Score"
173 printf "%-*s " nlen name
175 input_data <- Rapi.loadData name
176 let ldresult = input_data >>= Loader.mergeData
178 Bad err -> printf "\nError: failed to load data. \
181 let (nl, il, csf) = x
182 (_, fix_nl) = Loader.checkData nl il
183 putStrLn $ printCluster fix_nl il
184 when (optShowNodes opts) $
185 putStr $ Cluster.printNodes fix_nl
186 let ndata = serializeNodes csf nl
187 idata = serializeInstances csf nl il
188 oname = odir </> fixSlash name
189 writeFile (oname <.> "nodes") ndata
190 writeFile (oname <.> "instances") idata)