Style change: node and instance attributes
[ganeti-local] / hscan.hs
1 {-| Scan clusters via RAPI and write instance/node data files.
2
3 -}
4
5 {-
6
7 Copyright (C) 2009 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 Data.List
29 import Data.Function
30 import Monad
31 import System
32 import System.IO
33 import System.FilePath
34 import qualified System
35
36 import Text.Printf (printf)
37
38 import qualified Ganeti.HTools.Container as Container
39 import qualified Ganeti.HTools.Cluster as Cluster
40 import qualified Ganeti.HTools.Node as Node
41 import qualified Ganeti.HTools.Instance as Instance
42 import qualified Ganeti.HTools.Rapi as Rapi
43 import qualified Ganeti.HTools.Loader as Loader
44
45 import Ganeti.HTools.CLI
46 import Ganeti.HTools.Types
47
48 -- | Options list and functions
49 options :: [OptType]
50 options =
51     [ oPrintNodes
52     , oOutputDir
53     , oVerbose
54     , oNoHeaders
55     , oShowVer
56     , oShowHelp
57     ]
58
59 -- | Serialize a single node
60 serializeNode :: String -> Node.Node -> String
61 serializeNode csf node =
62     printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c" (Node.name node ++ csf)
63                (Node.tMem node) (Node.nMem node) (Node.fMem node)
64                (Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
65                (if Node.offline node then 'Y' else 'N')
66
67 -- | Generate node file data from node objects
68 serializeNodes :: String -> Node.List -> String
69 serializeNodes csf =
70     unlines . map (serializeNode csf) . Container.elems
71
72 -- | Serialize a single instance
73 serializeInstance :: String -> Node.List -> Instance.Instance -> String
74 serializeInstance csf nl inst =
75     let
76         iname = Instance.name inst ++ csf
77         pnode = Container.nameOf nl (Instance.pNode inst) ++ csf
78         sidx = Instance.sNode inst
79         snode = (if sidx == Node.noSecondary
80                     then ""
81                     else Container.nameOf nl sidx ++ csf)
82     in
83       printf "%s|%d|%d|%d|%s|%s|%s"
84              iname (Instance.mem inst) (Instance.dsk inst)
85              (Instance.vcpus inst) (Instance.runSt inst)
86              pnode snode
87
88 -- | Generate instance file data from instance objects
89 serializeInstances :: String -> Node.List -> Instance.List -> String
90 serializeInstances csf nl =
91     unlines . map (serializeInstance csf nl) . Container.elems
92
93 -- | Return a one-line summary of cluster state
94 printCluster :: Node.List -> Instance.List
95              -> String
96 printCluster nl il =
97     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
98         ccv = Cluster.compCV nl
99         nodes = Container.elems nl
100         insts = Container.elems il
101         t_ram = sum . map Node.tMem $ nodes
102         t_dsk = sum . map Node.tDsk $ nodes
103         f_ram = sum . map Node.fMem $ nodes
104         f_dsk = sum . map Node.fDsk $ nodes
105     in
106       printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
107                  (length nodes) (length insts)
108                  (length bad_nodes) (length bad_instances)
109                  t_ram f_ram
110                  (t_dsk / 1024) (f_dsk `div` 1024)
111                  ccv
112
113
114 -- | Replace slashes with underscore for saving to filesystem
115
116 fixSlash :: String -> String
117 fixSlash = map (\x -> if x == '/' then '_' else x)
118
119 -- | Main function.
120 main :: IO ()
121 main = do
122   cmd_args <- System.getArgs
123   (opts, clusters) <- parseOpts cmd_args "hscan" options
124
125   let odir = optOutPath opts
126       nlen = maximum . map length $ clusters
127
128   unless (optNoHeaders opts) $
129          printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
130                 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
131                 "t_disk" "f_disk" "Score"
132
133   mapM_ (\ name ->
134             do
135               printf "%-*s " nlen name
136               hFlush stdout
137               input_data <- Rapi.loadData name
138               let ldresult = input_data >>= Loader.mergeData
139               (case ldresult of
140                  Bad err -> printf "\nError: failed to load data. \
141                                    \Details:\n%s\n" err
142                  Ok x -> do
143                    let (nl, il, csf) = x
144                        (_, fix_nl) = Loader.checkData nl il
145                    putStrLn $ printCluster fix_nl il
146                    when (optShowNodes opts) $
147                         putStr $ Cluster.printNodes fix_nl
148                    let ndata = serializeNodes csf nl
149                        idata = serializeInstances csf nl il
150                        oname = odir </> fixSlash name
151                    writeFile (oname <.> "nodes") ndata
152                    writeFile (oname <.> "instances") idata)
153        ) clusters