1 {-| Scan clusters via RAPI and write instance/node data files.
5 module Main (main) where
12 import System.FilePath
13 import System.Console.GetOpt
14 import qualified System
16 import Text.Printf (printf)
18 import qualified Ganeti.HTools.Container as Container
19 import qualified Ganeti.HTools.Cluster as Cluster
20 import qualified Ganeti.HTools.Node as Node
21 import qualified Ganeti.HTools.Instance as Instance
22 import qualified Ganeti.HTools.CLI as CLI
23 import qualified Ganeti.HTools.Rapi as Rapi
24 import qualified Ganeti.HTools.Loader as Loader
25 import Ganeti.HTools.Types
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 , optNoHeader :: Bool -- ^ Do not show a header line
33 , optShowVer :: Bool -- ^ Just show the program version
34 , optShowHelp :: Bool -- ^ Just show the help
37 instance CLI.CLIOptions Options where
38 showVersion = optShowVer
39 showHelp = optShowHelp
41 -- | Default values for the command line options.
42 defaultOptions :: Options
43 defaultOptions = Options
44 { optShowNodes = False
52 -- | Options list and functions
53 options :: [OptDescr (Options -> Options)]
55 [ Option ['p'] ["print-nodes"]
56 (NoArg (\ opts -> opts { optShowNodes = True }))
57 "print the final node list"
58 , Option ['d'] ["output-dir"]
59 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
60 "directory in which to write output files"
61 , Option ['v'] ["verbose"]
62 (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
63 "increase the verbosity level"
64 , Option [] ["no-headers"]
65 (NoArg (\ opts -> opts { optNoHeader = True }))
66 "do not show a header line"
67 , Option ['V'] ["version"]
68 (NoArg (\ opts -> opts { optShowVer = True}))
69 "show the version of the program"
70 , Option ['h'] ["help"]
71 (NoArg (\ opts -> opts { optShowHelp = True}))
75 -- | Generate node file data from node objects
76 serializeNodes :: Node.List -> String -> String
77 serializeNodes nl csf =
78 let nodes = Container.elems nl
81 let name = Node.name node ++ csf
82 t_mem = (truncate $ Node.t_mem node)::Int
83 t_dsk = (truncate $ Node.t_dsk node)::Int
85 printf "%s|%d|%d|%d|%d|%d|%c" name
86 t_mem (Node.n_mem node) (Node.f_mem node)
87 t_dsk (Node.f_dsk node)
88 (if Node.offline node then 'Y' else 'N')
93 -- | Generate instance file data from instance objects
94 serializeInstances :: Node.List -> Instance.List
96 serializeInstances nl il csf =
97 let instances = Container.elems il
101 iname = Instance.name inst ++ csf
102 pnode = Container.nameOf nl $ Instance.pnode inst
103 snode = Container.nameOf nl $ Instance.snode inst
105 printf "%s|%d|%d|%s|%s|%s"
106 iname (Instance.mem inst) (Instance.dsk inst)
107 (Instance.run_st inst)
113 -- | Return a one-line summary of cluster state
114 printCluster :: Node.List -> Instance.List
117 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
118 ccv = Cluster.compCV nl
119 nodes = Container.elems nl
120 insts = Container.elems il
121 t_ram = truncate . sum . map Node.t_mem $ nodes
122 t_dsk = truncate . sum . map Node.t_dsk $ nodes
123 f_ram = sum . map Node.f_mem $ nodes
124 f_dsk = sum . map Node.f_dsk $ nodes
126 printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
127 (length nodes) (length insts)
128 (length bad_nodes) (length bad_instances)
129 (t_ram::Integer) f_ram
130 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
134 -- | Replace slashes with underscore for saving to filesystem
136 fixSlash :: String -> String
137 fixSlash = map (\x -> if x == '/' then '_' else x)
142 cmd_args <- System.getArgs
143 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
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 input_data <- Rapi.loadData name
159 let ldresult = input_data >>= Loader.mergeData
161 Bad err -> printf "\nError: failed to load data. \
164 let (nl, il, csf) = x
165 (_, fix_nl) = Loader.checkData nl il
166 putStrLn $ printCluster fix_nl il
167 when (optShowNodes opts) $ do
168 putStr $ Cluster.printNodes fix_nl
169 let ndata = serializeNodes nl csf
170 idata = serializeInstances nl il csf
171 oname = odir </> (fixSlash name)
172 writeFile (oname <.> "nodes") ndata
173 writeFile (oname <.> "instances") idata)