root / hscan.hs @ e2fa2baf
History | View | Annotate | Download (6.8 kB)
1 | 1b7a5835 | Iustin Pop | {-| Scan clusters via RAPI and write instance/node data files. |
---|---|---|---|
2 | 1b7a5835 | Iustin Pop | |
3 | 1b7a5835 | Iustin Pop | -} |
4 | 1b7a5835 | Iustin Pop | |
5 | e2fa2baf | Iustin Pop | {- |
6 | e2fa2baf | Iustin Pop | |
7 | e2fa2baf | Iustin Pop | Copyright (C) 2009 Google Inc. |
8 | e2fa2baf | Iustin Pop | |
9 | e2fa2baf | Iustin Pop | This program is free software; you can redistribute it and/or modify |
10 | e2fa2baf | Iustin Pop | it under the terms of the GNU General Public License as published by |
11 | e2fa2baf | Iustin Pop | the Free Software Foundation; either version 2 of the License, or |
12 | e2fa2baf | Iustin Pop | (at your option) any later version. |
13 | e2fa2baf | Iustin Pop | |
14 | e2fa2baf | Iustin Pop | This program is distributed in the hope that it will be useful, but |
15 | e2fa2baf | Iustin Pop | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | e2fa2baf | Iustin Pop | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | e2fa2baf | Iustin Pop | General Public License for more details. |
18 | e2fa2baf | Iustin Pop | |
19 | e2fa2baf | Iustin Pop | You should have received a copy of the GNU General Public License |
20 | e2fa2baf | Iustin Pop | along with this program; if not, write to the Free Software |
21 | e2fa2baf | Iustin Pop | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
22 | e2fa2baf | Iustin Pop | 02110-1301, USA. |
23 | e2fa2baf | Iustin Pop | |
24 | e2fa2baf | Iustin Pop | -} |
25 | e2fa2baf | Iustin Pop | |
26 | 1b7a5835 | Iustin Pop | module Main (main) where |
27 | 1b7a5835 | Iustin Pop | |
28 | 1b7a5835 | Iustin Pop | import Data.List |
29 | 1b7a5835 | Iustin Pop | import Data.Function |
30 | 1b7a5835 | Iustin Pop | import Monad |
31 | 1b7a5835 | Iustin Pop | import System |
32 | 1b7a5835 | Iustin Pop | import System.IO |
33 | 1b7a5835 | Iustin Pop | import System.FilePath |
34 | 1b7a5835 | Iustin Pop | import System.Console.GetOpt |
35 | 1b7a5835 | Iustin Pop | import qualified System |
36 | 1b7a5835 | Iustin Pop | |
37 | 1b7a5835 | Iustin Pop | import Text.Printf (printf) |
38 | 1b7a5835 | Iustin Pop | |
39 | 1b7a5835 | Iustin Pop | import qualified Ganeti.HTools.Container as Container |
40 | 1b7a5835 | Iustin Pop | import qualified Ganeti.HTools.Cluster as Cluster |
41 | 1b7a5835 | Iustin Pop | import qualified Ganeti.HTools.Node as Node |
42 | 1b7a5835 | Iustin Pop | import qualified Ganeti.HTools.Instance as Instance |
43 | 209b3711 | Iustin Pop | import qualified Ganeti.HTools.CLI as CLI |
44 | 040afc35 | Iustin Pop | import qualified Ganeti.HTools.Rapi as Rapi |
45 | e4c5beaf | Iustin Pop | import qualified Ganeti.HTools.Loader as Loader |
46 | e4c5beaf | Iustin Pop | import Ganeti.HTools.Types |
47 | 1b7a5835 | Iustin Pop | |
48 | 1b7a5835 | Iustin Pop | -- | Command line options structure. |
49 | 1b7a5835 | Iustin Pop | data Options = Options |
50 | 1b7a5835 | Iustin Pop | { optShowNodes :: Bool -- ^ Whether to show node status |
51 | 1b7a5835 | Iustin Pop | , optOutPath :: FilePath -- ^ Path to the output directory |
52 | 1b7a5835 | Iustin Pop | , optVerbose :: Int -- ^ Verbosity level |
53 | 1b7a5835 | Iustin Pop | , optNoHeader :: Bool -- ^ Do not show a header line |
54 | 209b3711 | Iustin Pop | , optShowVer :: Bool -- ^ Just show the program version |
55 | 209b3711 | Iustin Pop | , optShowHelp :: Bool -- ^ Just show the help |
56 | 1b7a5835 | Iustin Pop | } deriving Show |
57 | 1b7a5835 | Iustin Pop | |
58 | 75d1edf8 | Iustin Pop | instance CLI.CLIOptions Options where |
59 | 75d1edf8 | Iustin Pop | showVersion = optShowVer |
60 | 75d1edf8 | Iustin Pop | showHelp = optShowHelp |
61 | 75d1edf8 | Iustin Pop | |
62 | 1b7a5835 | Iustin Pop | -- | Default values for the command line options. |
63 | 1b7a5835 | Iustin Pop | defaultOptions :: Options |
64 | 1b7a5835 | Iustin Pop | defaultOptions = Options |
65 | 1b7a5835 | Iustin Pop | { optShowNodes = False |
66 | 1b7a5835 | Iustin Pop | , optOutPath = "." |
67 | 1b7a5835 | Iustin Pop | , optVerbose = 0 |
68 | 1b7a5835 | Iustin Pop | , optNoHeader = False |
69 | 209b3711 | Iustin Pop | , optShowVer = False |
70 | 209b3711 | Iustin Pop | , optShowHelp = False |
71 | 1b7a5835 | Iustin Pop | } |
72 | 1b7a5835 | Iustin Pop | |
73 | 1b7a5835 | Iustin Pop | -- | Options list and functions |
74 | 1b7a5835 | Iustin Pop | options :: [OptDescr (Options -> Options)] |
75 | 1b7a5835 | Iustin Pop | options = |
76 | 1b7a5835 | Iustin Pop | [ Option ['p'] ["print-nodes"] |
77 | 1b7a5835 | Iustin Pop | (NoArg (\ opts -> opts { optShowNodes = True })) |
78 | 1b7a5835 | Iustin Pop | "print the final node list" |
79 | 1b7a5835 | Iustin Pop | , Option ['d'] ["output-dir"] |
80 | 1b7a5835 | Iustin Pop | (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH") |
81 | 1b7a5835 | Iustin Pop | "directory in which to write output files" |
82 | 1b7a5835 | Iustin Pop | , Option ['v'] ["verbose"] |
83 | 1b7a5835 | Iustin Pop | (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 })) |
84 | 1b7a5835 | Iustin Pop | "increase the verbosity level" |
85 | 1b7a5835 | Iustin Pop | , Option [] ["no-headers"] |
86 | 1b7a5835 | Iustin Pop | (NoArg (\ opts -> opts { optNoHeader = True })) |
87 | 1b7a5835 | Iustin Pop | "do not show a header line" |
88 | 209b3711 | Iustin Pop | , Option ['V'] ["version"] |
89 | 209b3711 | Iustin Pop | (NoArg (\ opts -> opts { optShowVer = True})) |
90 | 209b3711 | Iustin Pop | "show the version of the program" |
91 | 209b3711 | Iustin Pop | , Option ['h'] ["help"] |
92 | 209b3711 | Iustin Pop | (NoArg (\ opts -> opts { optShowHelp = True})) |
93 | 209b3711 | Iustin Pop | "show help" |
94 | 1b7a5835 | Iustin Pop | ] |
95 | 1b7a5835 | Iustin Pop | |
96 | 1b7a5835 | Iustin Pop | -- | Generate node file data from node objects |
97 | 262a08a2 | Iustin Pop | serializeNodes :: Node.List -> String -> String |
98 | dbd6700b | Iustin Pop | serializeNodes nl csf = |
99 | dbd6700b | Iustin Pop | let nodes = Container.elems nl |
100 | 1b7a5835 | Iustin Pop | nlines = map |
101 | 1b7a5835 | Iustin Pop | (\node -> |
102 | dbd6700b | Iustin Pop | let name = Node.name node ++ csf |
103 | 670b57ad | Iustin Pop | t_mem = (truncate $ Node.t_mem node)::Int |
104 | 670b57ad | Iustin Pop | t_dsk = (truncate $ Node.t_dsk node)::Int |
105 | 1b7a5835 | Iustin Pop | in |
106 | 00b15752 | Iustin Pop | printf "%s|%d|%d|%d|%d|%d|%c" name |
107 | 04be800a | Iustin Pop | t_mem (Node.n_mem node) (Node.f_mem node) |
108 | 00b15752 | Iustin Pop | t_dsk (Node.f_dsk node) |
109 | 00b15752 | Iustin Pop | (if Node.offline node then 'Y' else 'N') |
110 | 00b15752 | Iustin Pop | ) |
111 | 1b7a5835 | Iustin Pop | nodes |
112 | 1b7a5835 | Iustin Pop | in unlines nlines |
113 | 1b7a5835 | Iustin Pop | |
114 | 1b7a5835 | Iustin Pop | -- | Generate instance file data from instance objects |
115 | 262a08a2 | Iustin Pop | serializeInstances :: Node.List -> Instance.List |
116 | db1bcfe8 | Iustin Pop | -> String -> String |
117 | db1bcfe8 | Iustin Pop | serializeInstances nl il csf = |
118 | db1bcfe8 | Iustin Pop | let instances = Container.elems il |
119 | 1b7a5835 | Iustin Pop | nlines = map |
120 | 1b7a5835 | Iustin Pop | (\inst -> |
121 | 1b7a5835 | Iustin Pop | let |
122 | dbd6700b | Iustin Pop | iname = Instance.name inst ++ csf |
123 | 262a08a2 | Iustin Pop | pnode = Container.nameOf nl $ Instance.pnode inst |
124 | 262a08a2 | Iustin Pop | snode = Container.nameOf nl $ Instance.snode inst |
125 | 1b7a5835 | Iustin Pop | in |
126 | f82f1f39 | Iustin Pop | printf "%s|%d|%d|%s|%s|%s" |
127 | 1b7a5835 | Iustin Pop | iname (Instance.mem inst) (Instance.dsk inst) |
128 | f82f1f39 | Iustin Pop | (Instance.run_st inst) |
129 | 1b7a5835 | Iustin Pop | pnode snode |
130 | 1b7a5835 | Iustin Pop | ) |
131 | 1b7a5835 | Iustin Pop | instances |
132 | 1b7a5835 | Iustin Pop | in unlines nlines |
133 | 1b7a5835 | Iustin Pop | |
134 | 1b7a5835 | Iustin Pop | -- | Return a one-line summary of cluster state |
135 | 262a08a2 | Iustin Pop | printCluster :: Node.List -> Instance.List |
136 | 1b7a5835 | Iustin Pop | -> String |
137 | dbd6700b | Iustin Pop | printCluster nl il = |
138 | 1b7a5835 | Iustin Pop | let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il |
139 | 1b7a5835 | Iustin Pop | ccv = Cluster.compCV nl |
140 | 1b7a5835 | Iustin Pop | nodes = Container.elems nl |
141 | dbd6700b | Iustin Pop | insts = Container.elems il |
142 | 1b7a5835 | Iustin Pop | t_ram = truncate . sum . map Node.t_mem $ nodes |
143 | 1b7a5835 | Iustin Pop | t_dsk = truncate . sum . map Node.t_dsk $ nodes |
144 | 1b7a5835 | Iustin Pop | f_ram = sum . map Node.f_mem $ nodes |
145 | 1b7a5835 | Iustin Pop | f_dsk = sum . map Node.f_dsk $ nodes |
146 | 1b7a5835 | Iustin Pop | in |
147 | 1b7a5835 | Iustin Pop | printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f" |
148 | dbd6700b | Iustin Pop | (length nodes) (length insts) |
149 | 1b7a5835 | Iustin Pop | (length bad_nodes) (length bad_instances) |
150 | 1b7a5835 | Iustin Pop | (t_ram::Integer) f_ram |
151 | 1b7a5835 | Iustin Pop | ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024) |
152 | 1b7a5835 | Iustin Pop | ccv |
153 | 1b7a5835 | Iustin Pop | |
154 | 1b7a5835 | Iustin Pop | |
155 | 0944090a | Iustin Pop | -- | Replace slashes with underscore for saving to filesystem |
156 | 0944090a | Iustin Pop | |
157 | 0944090a | Iustin Pop | fixSlash :: String -> String |
158 | 0944090a | Iustin Pop | fixSlash = map (\x -> if x == '/' then '_' else x) |
159 | 0944090a | Iustin Pop | |
160 | 1b7a5835 | Iustin Pop | -- | Main function. |
161 | 1b7a5835 | Iustin Pop | main :: IO () |
162 | 1b7a5835 | Iustin Pop | main = do |
163 | 1b7a5835 | Iustin Pop | cmd_args <- System.getArgs |
164 | 209b3711 | Iustin Pop | (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options |
165 | 75d1edf8 | Iustin Pop | defaultOptions |
166 | 1b7a5835 | Iustin Pop | |
167 | 1b7a5835 | Iustin Pop | let odir = optOutPath opts |
168 | 1b7a5835 | Iustin Pop | nlen = maximum . map length $ clusters |
169 | 1b7a5835 | Iustin Pop | |
170 | 1b7a5835 | Iustin Pop | unless (optNoHeader opts) $ |
171 | 1b7a5835 | Iustin Pop | printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen |
172 | 1b7a5835 | Iustin Pop | "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem" |
173 | 1b7a5835 | Iustin Pop | "t_disk" "f_disk" "Score" |
174 | 1b7a5835 | Iustin Pop | |
175 | fd22ce8e | Iustin Pop | mapM_ (\ name -> |
176 | 1b7a5835 | Iustin Pop | do |
177 | 1b7a5835 | Iustin Pop | printf "%-*s " nlen name |
178 | 1b7a5835 | Iustin Pop | hFlush stdout |
179 | 040afc35 | Iustin Pop | input_data <- Rapi.loadData name |
180 | e4c5beaf | Iustin Pop | let ldresult = input_data >>= Loader.mergeData |
181 | fd22ce8e | Iustin Pop | (case ldresult of |
182 | fd22ce8e | Iustin Pop | Bad err -> printf "\nError: failed to load data. \ |
183 | fd22ce8e | Iustin Pop | \Details:\n%s\n" err |
184 | fd22ce8e | Iustin Pop | Ok x -> do |
185 | 8472a321 | Iustin Pop | let (nl, il, csf) = x |
186 | dbd6700b | Iustin Pop | (_, fix_nl) = Loader.checkData nl il |
187 | dbd6700b | Iustin Pop | putStrLn $ printCluster fix_nl il |
188 | fd22ce8e | Iustin Pop | when (optShowNodes opts) $ do |
189 | dbd6700b | Iustin Pop | putStr $ Cluster.printNodes fix_nl |
190 | dbd6700b | Iustin Pop | let ndata = serializeNodes nl csf |
191 | db1bcfe8 | Iustin Pop | idata = serializeInstances nl il csf |
192 | fd22ce8e | Iustin Pop | oname = odir </> (fixSlash name) |
193 | fd22ce8e | Iustin Pop | writeFile (oname <.> "nodes") ndata |
194 | fd22ce8e | Iustin Pop | writeFile (oname <.> "instances") idata) |
195 | 1b7a5835 | Iustin Pop | ) clusters |