1 {-| Scan clusters via RAPI or LUXI and write state data files.
7 Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 module Ganeti.HTools.Program.Hscan (main, options) where
29 import Data.Maybe (isJust, fromJust, fromMaybe)
32 import System.FilePath
34 import Text.Printf (printf)
36 import qualified Ganeti.HTools.Container as Container
37 import qualified Ganeti.HTools.Cluster as Cluster
38 import qualified Ganeti.HTools.Node as Node
39 import qualified Ganeti.HTools.Instance as Instance
40 import qualified Ganeti.HTools.Rapi as Rapi
41 import qualified Ganeti.HTools.Luxi as Luxi
42 import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
43 import Ganeti.HTools.Text (serializeCluster)
45 import Ganeti.HTools.CLI
46 import Ganeti.HTools.Types
48 -- | Options list and functions.
60 -- | Return a one-line summary of cluster state.
61 printCluster :: Node.List -> Instance.List
64 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
65 ccv = Cluster.compCV nl
66 nodes = Container.elems nl
67 insts = Container.elems il
68 t_ram = sum . map Node.tMem $ nodes
69 t_dsk = sum . map Node.tDsk $ nodes
70 f_ram = sum . map Node.fMem $ nodes
71 f_dsk = sum . map Node.fDsk $ nodes
72 in printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
73 (length nodes) (length insts)
74 (length bad_nodes) (length bad_instances)
75 t_ram f_ram (t_dsk / 1024) (f_dsk `div` 1024) ccv
77 -- | Replace slashes with underscore for saving to filesystem.
78 fixSlash :: String -> String
79 fixSlash = map (\x -> if x == '/' then '_' else x)
81 -- | Generates serialized data from loader input.
82 processData :: ClusterData -> Result ClusterData
83 processData input_data = do
84 cdata@(ClusterData _ nl il _ _) <- mergeData [] [] [] [] input_data
85 let (_, fix_nl) = checkData nl il
86 return cdata { cdNodes = fix_nl }
88 -- | Writes cluster data out.
94 writeData _ name _ (Bad err) =
95 printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
98 writeData nlen name opts (Ok cdata) = do
99 let fixdata = processData cdata
101 Bad err -> printf "\nError for %s: failed to process data. Details:\n%s\n"
102 name err >> return False
103 Ok processed -> writeDataInner nlen name opts cdata processed
105 -- | Inner function for writing cluster data to disk.
106 writeDataInner :: Int
112 writeDataInner nlen name opts cdata fixdata = do
113 let (ClusterData _ nl il _ _) = fixdata
114 printf "%-*s " nlen name :: IO ()
116 let shownodes = optShowNodes opts
117 odir = optOutPath opts
118 oname = odir </> fixSlash name
119 putStrLn $ printCluster nl il
121 when (isJust shownodes) $
122 putStr $ Cluster.printNodes nl (fromJust shownodes)
123 writeFile (oname <.> "data") (serializeCluster cdata)
127 main :: Options -> [String] -> IO ()
128 main opts clusters = do
131 let nlen = if null clusters
133 else maximum . map length $ clusters
135 unless (optNoHeaders opts) $
136 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
137 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
138 "t_disk" "f_disk" "Score"
140 when (null clusters) $ do
141 let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
143 input_data <- Luxi.loadData lsock
144 result <- writeData nlen name opts input_data
145 unless result $ exitWith $ ExitFailure 2
147 results <- mapM (\name -> Rapi.loadData name >>= writeData nlen name opts)
149 unless (all id results) $ exitWith (ExitFailure 2)