htools: simplify some JSON-related code
[ganeti-local] / htools / hscan.hs
1 {-| Scan clusters via RAPI or LUXI and write state data files.
2
3 -}
4
5 {-
6
7 Copyright (C) 2009, 2010, 2011 Google Inc.
8
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.
13
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.
18
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
22 02110-1301, USA.
23
24 -}
25
26 module Main (main) where
27
28 import Control.Monad
29 import Data.Maybe (isJust, fromJust, fromMaybe)
30 import System (exitWith, ExitCode(..))
31 import System.IO
32 import System.FilePath
33 import qualified System
34
35 import Text.Printf (printf)
36
37 import qualified Ganeti.HTools.Container as Container
38 import qualified Ganeti.HTools.Cluster as Cluster
39 import qualified Ganeti.HTools.Node as Node
40 import qualified Ganeti.HTools.Instance as Instance
41 import qualified Ganeti.HTools.Rapi as Rapi
42 import qualified Ganeti.HTools.Luxi as Luxi
43 import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
44 import Ganeti.HTools.Text (serializeCluster)
45
46 import Ganeti.HTools.CLI
47 import Ganeti.HTools.Types
48
49 -- | Options list and functions
50 options :: [OptType]
51 options =
52     [ oPrintNodes
53     , oOutputDir
54     , oLuxiSocket
55     , oVerbose
56     , oNoHeaders
57     , oShowVer
58     , oShowHelp
59     ]
60
61 -- | Return a one-line summary of cluster state
62 printCluster :: Node.List -> Instance.List
63              -> String
64 printCluster nl il =
65     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
66         ccv = Cluster.compCV nl
67         nodes = Container.elems nl
68         insts = Container.elems il
69         t_ram = sum . map Node.tMem $ nodes
70         t_dsk = sum . map Node.tDsk $ nodes
71         f_ram = sum . map Node.fMem $ nodes
72         f_dsk = sum . map Node.fDsk $ nodes
73     in
74       printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
75                  (length nodes) (length insts)
76                  (length bad_nodes) (length bad_instances)
77                  t_ram f_ram
78                  (t_dsk / 1024) (f_dsk `div` 1024)
79                  ccv
80
81
82 -- | Replace slashes with underscore for saving to filesystem
83 fixSlash :: String -> String
84 fixSlash = map (\x -> if x == '/' then '_' else x)
85
86
87 -- | Generates serialized data from loader input.
88 processData :: ClusterData -> Result ClusterData
89 processData input_data = do
90   cdata@(ClusterData _ nl il _) <- mergeData [] [] [] [] input_data
91   let (_, fix_nl) = checkData nl il
92   return cdata { cdNodes = fix_nl }
93
94 -- | Writes cluster data out
95 writeData :: Int
96           -> String
97           -> Options
98           -> Result ClusterData
99           -> IO Bool
100 writeData _ name _ (Bad err) =
101   printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
102   return False
103
104 writeData nlen name opts (Ok cdata) = do
105   let fixdata = processData cdata
106   case fixdata of
107     Bad err -> printf "\nError for %s: failed to process data. Details:\n%s\n"
108                name err >> return False
109     Ok processed -> writeDataInner nlen name opts cdata processed
110
111 writeDataInner :: Int
112                -> String
113                -> Options
114                -> ClusterData
115                -> ClusterData
116                -> IO Bool
117 writeDataInner nlen name opts cdata fixdata = do
118   let (ClusterData _ nl il _) = fixdata
119   printf "%-*s " nlen name :: IO ()
120   hFlush stdout
121   let shownodes = optShowNodes opts
122       odir = optOutPath opts
123       oname = odir </> fixSlash name
124   putStrLn $ printCluster nl il
125   hFlush stdout
126   when (isJust shownodes) $
127        putStr $ Cluster.printNodes nl (fromJust shownodes)
128   writeFile (oname <.> "data") (serializeCluster cdata)
129   return True
130
131 -- | Main function.
132 main :: IO ()
133 main = do
134   cmd_args <- System.getArgs
135   (opts, clusters) <- parseOpts cmd_args "hscan" options
136   let local = "LOCAL"
137
138   let nlen = if null clusters
139              then length local
140              else maximum . map length $ clusters
141
142   unless (optNoHeaders opts) $
143          printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
144                 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
145                 "t_disk" "f_disk" "Score"
146
147   when (null clusters) $ do
148          let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
149          let name = local
150          input_data <- Luxi.loadData lsock
151          result <- writeData nlen name opts input_data
152          unless result $ exitWith $ ExitFailure 2
153
154   results <- mapM (\name -> Rapi.loadData name >>= writeData nlen name opts)
155              clusters
156   unless (all id results) $ exitWith (ExitFailure 2)