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.Node as Node
22 import qualified Ganeti.HTools.Instance as Instance
23 import qualified Ganeti.HTools.CLI as CLI
24 import qualified Ganeti.HTools.Rapi as Rapi
25 import qualified Ganeti.HTools.Loader as Loader
26 import Ganeti.HTools.Types
28 -- | Command line options structure.
29 data Options = Options
30 { optShowNodes :: Bool -- ^ Whether to show node status
31 , optOutPath :: FilePath -- ^ Path to the output directory
32 , optVerbose :: Int -- ^ Verbosity level
33 , optNoHeader :: Bool -- ^ Do not show a header line
34 , optShowVer :: Bool -- ^ Just show the program version
35 , optShowHelp :: Bool -- ^ Just show the help
38 instance CLI.CLIOptions Options where
39 showVersion = optShowVer
40 showHelp = optShowHelp
42 -- | Default values for the command line options.
43 defaultOptions :: Options
44 defaultOptions = Options
45 { optShowNodes = False
53 -- | Options list and functions
54 options :: [OptDescr (Options -> Options)]
56 [ Option ['p'] ["print-nodes"]
57 (NoArg (\ opts -> opts { optShowNodes = True }))
58 "print the final node list"
59 , Option ['d'] ["output-dir"]
60 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
61 "directory in which to write output files"
62 , Option ['v'] ["verbose"]
63 (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
64 "increase the verbosity level"
65 , Option [] ["no-headers"]
66 (NoArg (\ opts -> opts { optNoHeader = True }))
67 "do not show a header line"
68 , Option ['V'] ["version"]
69 (NoArg (\ opts -> opts { optShowVer = True}))
70 "show the version of the program"
71 , Option ['h'] ["help"]
72 (NoArg (\ opts -> opts { optShowHelp = True}))
76 -- | Generate node file data from node objects
77 serializeNodes :: Cluster.NodeList -> String -> String
78 serializeNodes nl csf =
79 let nodes = Container.elems nl
82 let name = Node.name node ++ csf
83 t_mem = (truncate $ Node.t_mem node)::Int
84 t_dsk = (truncate $ Node.t_dsk node)::Int
86 printf "%s|%d|%d|%d|%d|%d|%c" name
87 t_mem (Node.n_mem node) (Node.f_mem node)
88 t_dsk (Node.f_dsk node)
89 (if Node.offline node then 'Y' else 'N')
94 -- | Generate instance file data from instance objects
95 serializeInstances :: Cluster.NodeList -> Cluster.InstanceList
97 serializeInstances nl il csf =
98 let instances = Container.elems il
102 iname = Instance.name inst ++ csf
103 pnode = cNameOf nl $ Instance.pnode inst
104 snode = cNameOf nl $ Instance.snode inst
106 printf "%s|%d|%d|%s|%s|%s"
107 iname (Instance.mem inst) (Instance.dsk inst)
108 (Instance.run_st inst)
114 -- | Return a one-line summary of cluster state
115 printCluster :: Cluster.NodeList -> Cluster.InstanceList
118 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
119 ccv = Cluster.compCV nl
120 nodes = Container.elems nl
121 insts = Container.elems il
122 t_ram = truncate . sum . map Node.t_mem $ nodes
123 t_dsk = truncate . sum . map Node.t_dsk $ nodes
124 f_ram = sum . map Node.f_mem $ nodes
125 f_dsk = sum . map Node.f_dsk $ nodes
127 printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
128 (length nodes) (length insts)
129 (length bad_nodes) (length bad_instances)
130 (t_ram::Integer) f_ram
131 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
135 -- | Replace slashes with underscore for saving to filesystem
137 fixSlash :: String -> String
138 fixSlash = map (\x -> if x == '/' then '_' else x)
143 cmd_args <- System.getArgs
144 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
147 let odir = optOutPath opts
148 nlen = maximum . map length $ clusters
150 unless (optNoHeader opts) $
151 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
152 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
153 "t_disk" "f_disk" "Score"
157 printf "%-*s " nlen name
159 input_data <- Rapi.loadData name
160 let ldresult = input_data >>= Loader.mergeData
162 Bad err -> printf "\nError: failed to load data. \
165 let (nl, il, csf, _, _) = x
166 (_, fix_nl) = Loader.checkData nl il
167 putStrLn $ printCluster fix_nl il
168 when (optShowNodes opts) $ do
169 putStr $ Cluster.printNodes fix_nl
170 let ndata = serializeNodes nl csf
171 idata = serializeInstances nl il csf
172 oname = odir </> (fixSlash name)
173 writeFile (oname <.> "nodes") ndata
174 writeFile (oname <.> "instances") idata)