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 -> Cluster.NameList -> String
78 serializeNodes nl csf ktn =
79 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
80 nodes = Container.elems nl
83 let name = (fromJust $ lookup (Node.idx node) etn)
84 t_mem = (truncate $ Node.t_mem node)::Int
85 t_dsk = (truncate $ Node.t_dsk node)::Int
87 printf "%s|%d|%d|%d|%d|%d|%c" name
88 t_mem (Node.n_mem node) (Node.f_mem node)
89 t_dsk (Node.f_dsk node)
90 (if Node.offline node then 'Y' else 'N')
95 -- | Generate instance file data from instance objects
96 serializeInstances :: Cluster.InstanceList -> String
97 -> Cluster.NameList -> Cluster.NameList -> String
98 serializeInstances il csf ktn kti =
99 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
100 eti = map (\(idx, name) -> (idx, name ++ csf)) kti
101 instances = Container.elems il
105 iname = fromJust $ lookup (Instance.idx inst) eti
106 pnode = fromJust $ lookup (Instance.pnode inst) etn
107 snode = fromJust $ lookup (Instance.snode inst) etn
109 printf "%s|%d|%d|%s|%s|%s"
110 iname (Instance.mem inst) (Instance.dsk inst)
111 (Instance.run_st inst)
117 -- | Return a one-line summary of cluster state
118 printCluster :: Cluster.NodeList -> Cluster.InstanceList
119 -> Cluster.NameList -> Cluster.NameList
121 printCluster nl il ktn kti =
122 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
123 ccv = Cluster.compCV nl
124 nodes = Container.elems nl
125 t_ram = truncate . sum . map Node.t_mem $ nodes
126 t_dsk = truncate . sum . map Node.t_dsk $ nodes
127 f_ram = sum . map Node.f_mem $ nodes
128 f_dsk = sum . map Node.f_dsk $ nodes
130 printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
131 (length ktn) (length kti)
132 (length bad_nodes) (length bad_instances)
133 (t_ram::Integer) f_ram
134 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
138 -- | Replace slashes with underscore for saving to filesystem
140 fixSlash :: String -> String
141 fixSlash = map (\x -> if x == '/' then '_' else x)
146 cmd_args <- System.getArgs
147 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
150 let odir = optOutPath opts
151 nlen = maximum . map length $ clusters
153 unless (optNoHeader opts) $
154 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
155 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
156 "t_disk" "f_disk" "Score"
160 printf "%-*s " nlen name
162 input_data <- Rapi.loadData name
163 let ldresult = input_data >>= Loader.mergeData
165 Bad err -> printf "\nError: failed to load data. \
168 let (nl, il, csf, ktn, kti) = x
169 (_, fix_nl) = Loader.checkData nl il ktn kti
170 putStrLn $ printCluster fix_nl il ktn kti
171 when (optShowNodes opts) $ do
172 putStr $ Cluster.printNodes ktn fix_nl
173 let ndata = serializeNodes nl csf ktn
174 idata = serializeInstances il csf ktn kti
175 oname = odir </> (fixSlash name)
176 writeFile (oname <.> "nodes") ndata
177 writeFile (oname <.> "instances") idata)