3 {-| Scan clusters via RAPI or LUXI and write state data files.
9 Copyright (C) 2009 Google Inc.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 module Main (main) where
31 import Data.Maybe (isJust, fromJust, fromMaybe)
34 import System (exitWith, ExitCode(..))
37 import System.FilePath
38 import qualified System
40 import Text.Printf (printf)
42 import qualified Ganeti.HTools.Container as Container
43 import qualified Ganeti.HTools.Cluster as Cluster
44 import qualified Ganeti.HTools.Node as Node
45 import qualified Ganeti.HTools.Instance as Instance
47 import qualified Ganeti.HTools.Rapi as Rapi
49 import qualified Ganeti.HTools.Luxi as Luxi
50 import qualified Ganeti.HTools.Loader as Loader
52 import Ganeti.HTools.CLI
53 import Ganeti.HTools.Types
55 -- | Options list and functions
67 -- | Serialize a single node
68 serializeNode :: Node.Node -> String
70 printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c" (Node.name node)
71 (Node.tMem node) (Node.nMem node) (Node.fMem node)
72 (Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
73 (if Node.offline node then 'Y' else 'N')
75 -- | Generate node file data from node objects
76 serializeNodes :: Node.List -> String
77 serializeNodes = unlines . map serializeNode . Container.elems
79 -- | Serialize a single instance
80 serializeInstance :: Node.List -> Instance.Instance -> String
81 serializeInstance nl inst =
83 iname = Instance.name inst
84 pnode = Container.nameOf nl (Instance.pNode inst)
85 sidx = Instance.sNode inst
86 snode = (if sidx == Node.noSecondary
88 else Container.nameOf nl sidx)
90 printf "%s|%d|%d|%d|%s|%s|%s|%s"
91 iname (Instance.mem inst) (Instance.dsk inst)
92 (Instance.vcpus inst) (Instance.runSt inst)
93 pnode snode (intercalate "," (Instance.tags inst))
95 -- | Generate instance file data from instance objects
96 serializeInstances :: Node.List -> Instance.List -> String
97 serializeInstances nl =
98 unlines . map (serializeInstance nl) . Container.elems
100 -- | Return a one-line summary of cluster state
101 printCluster :: Node.List -> Instance.List
104 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
105 ccv = Cluster.compCV nl
106 nodes = Container.elems nl
107 insts = Container.elems il
108 t_ram = sum . map Node.tMem $ nodes
109 t_dsk = sum . map Node.tDsk $ nodes
110 f_ram = sum . map Node.fMem $ nodes
111 f_dsk = sum . map Node.fDsk $ nodes
113 printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
114 (length nodes) (length insts)
115 (length bad_nodes) (length bad_instances)
117 (t_dsk / 1024) (f_dsk `div` 1024)
121 -- | Replace slashes with underscore for saving to filesystem
122 fixSlash :: String -> String
123 fixSlash = map (\x -> if x == '/' then '_' else x)
126 -- | Generates serialized data from loader input
127 processData :: Result (Node.AssocList, Instance.AssocList, [String])
128 -> Result (Node.List, Instance.List, String)
129 processData input_data = do
130 (nl, il, _) <- input_data >>= Loader.mergeData [] [] []
131 let (_, fix_nl) = Loader.checkData nl il
132 let ndata = serializeNodes nl
133 idata = serializeInstances nl il
134 adata = ndata ++ ['\n'] ++ idata
135 return (fix_nl, il, adata)
137 -- | Writes cluster data out
141 -> Result (Node.List, Instance.List, String)
143 writeData _ name _ (Bad err) =
144 printf "\nError for %s: failed to load data. Details:\n%s\n" name err
146 writeData nlen name opts (Ok (nl, il, adata)) = do
147 printf "%-*s " nlen name :: IO ()
149 let shownodes = optShowNodes opts
150 odir = optOutPath opts
151 oname = odir </> fixSlash name
152 putStrLn $ printCluster nl il
154 when (isJust shownodes) $
155 putStr $ Cluster.printNodes nl (fromJust shownodes)
156 writeFile (oname <.> "data") adata
162 cmd_args <- System.getArgs
163 (opts, clusters) <- parseOpts cmd_args "hscan" options
166 let nlen = if null clusters
168 else maximum . map length $ clusters
170 unless (optNoHeaders opts) $
171 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
172 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
173 "t_disk" "f_disk" "Score"
175 when (null clusters) $ do
176 let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
178 input_data <- Luxi.loadData lsock
179 writeData nlen name opts (processData input_data)
184 input_data <- Rapi.loadData name
185 writeData nlen name opts (processData input_data)
188 when (not $ null clusters) $ do
189 putStrLn "RAPI/curl backend disabled at compile time, cannot scan clusters"
190 exitWith $ ExitFailure 1