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.InstanceList -> String
96 -> Cluster.NameList -> String
97 serializeInstances il csf ktn =
98 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
99 instances = Container.elems il
103 iname = Instance.name inst ++ csf
104 pnode = fromJust $ lookup (Instance.pnode inst) etn
105 snode = fromJust $ lookup (Instance.snode inst) etn
107 printf "%s|%d|%d|%s|%s|%s"
108 iname (Instance.mem inst) (Instance.dsk inst)
109 (Instance.run_st inst)
115 -- | Return a one-line summary of cluster state
116 printCluster :: Cluster.NodeList -> Cluster.InstanceList
119 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
120 ccv = Cluster.compCV nl
121 nodes = Container.elems nl
122 insts = Container.elems il
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 nodes) (length insts)
130 (length bad_nodes) (length bad_instances)
131 (t_ram::Integer) f_ram
132 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
136 -- | Replace slashes with underscore for saving to filesystem
138 fixSlash :: String -> String
139 fixSlash = map (\x -> if x == '/' then '_' else x)
144 cmd_args <- System.getArgs
145 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
148 let odir = optOutPath opts
149 nlen = maximum . map length $ clusters
151 unless (optNoHeader opts) $
152 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
153 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
154 "t_disk" "f_disk" "Score"
158 printf "%-*s " nlen name
160 input_data <- Rapi.loadData name
161 let ldresult = input_data >>= Loader.mergeData
163 Bad err -> printf "\nError: failed to load data. \
166 let (nl, il, csf, ktn, _) = x
167 (_, fix_nl) = Loader.checkData nl il
168 putStrLn $ printCluster fix_nl il
169 when (optShowNodes opts) $ do
170 putStr $ Cluster.printNodes fix_nl
171 let ndata = serializeNodes nl csf
172 idata = serializeInstances il csf ktn
173 oname = odir </> (fixSlash name)
174 writeFile (oname <.> "nodes") ndata
175 writeFile (oname <.> "instances") idata)