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