Small documentation update
[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 System.Console.GetOpt
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.CLI as CLI
44 import qualified Ganeti.HTools.Rapi as Rapi
45 import qualified Ganeti.HTools.Loader as Loader
46 import Ganeti.HTools.Types
47
48 -- | Command line options structure.
49 data Options = Options
50     { optShowNodes :: Bool     -- ^ Whether to show node status
51     , optOutPath   :: FilePath -- ^ Path to the output directory
52     , optVerbose   :: Int      -- ^ Verbosity level
53     , optNoHeader  :: Bool     -- ^ Do not show a header line
54     , optShowVer   :: Bool     -- ^ Just show the program version
55     , optShowHelp  :: Bool     -- ^ Just show the help
56     } deriving Show
57
58 instance CLI.CLIOptions Options where
59     showVersion = optShowVer
60     showHelp    = optShowHelp
61
62 -- | Default values for the command line options.
63 defaultOptions :: Options
64 defaultOptions  = Options
65  { optShowNodes = False
66  , optOutPath   = "."
67  , optVerbose   = 0
68  , optNoHeader  = False
69  , optShowVer   = False
70  , optShowHelp  = False
71  }
72
73 -- | Options list and functions
74 options :: [OptDescr (Options -> Options)]
75 options =
76     [ Option ['p']     ["print-nodes"]
77       (NoArg (\ opts -> opts { optShowNodes = True }))
78       "print the final node list"
79     , Option ['d']     ["output-dir"]
80       (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
81       "directory in which to write output files"
82     , Option ['v']     ["verbose"]
83       (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
84       "increase the verbosity level"
85     , Option []        ["no-headers"]
86       (NoArg (\ opts -> opts { optNoHeader = True }))
87       "do not show a header line"
88     , Option ['V']     ["version"]
89       (NoArg (\ opts -> opts { optShowVer = True}))
90       "show the version of the program"
91     , Option ['h']     ["help"]
92       (NoArg (\ opts -> opts { optShowHelp = True}))
93       "show help"
94     ]
95
96 -- | Generate node file data from node objects
97 serializeNodes :: Node.List -> String -> String
98 serializeNodes nl csf =
99     let nodes = Container.elems nl
100         nlines = map
101                  (\node ->
102                       let name = Node.name node ++ csf
103                           t_mem = (truncate $ Node.t_mem node)::Int
104                           t_dsk = (truncate $ Node.t_dsk node)::Int
105                       in
106                         printf "%s|%d|%d|%d|%d|%d|%c" name
107                                    t_mem (Node.n_mem node) (Node.f_mem node)
108                                    t_dsk (Node.f_dsk node)
109                                    (if Node.offline node then 'Y' else 'N')
110                  )
111                  nodes
112     in unlines nlines
113
114 -- | Generate instance file data from instance objects
115 serializeInstances :: Node.List -> Instance.List
116                    -> String -> String
117 serializeInstances nl il csf =
118     let instances = Container.elems il
119         nlines = map
120                  (\inst ->
121                       let
122                           iname = Instance.name inst ++ csf
123                           pnode = Container.nameOf nl $ Instance.pnode inst
124                           snode = Container.nameOf nl $ Instance.snode inst
125                       in
126                         printf "%s|%d|%d|%s|%s|%s"
127                                iname (Instance.mem inst) (Instance.dsk inst)
128                                (Instance.run_st inst)
129                                pnode snode
130                  )
131                  instances
132     in unlines nlines
133
134 -- | Return a one-line summary of cluster state
135 printCluster :: Node.List -> Instance.List
136              -> String
137 printCluster nl il =
138     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
139         ccv = Cluster.compCV nl
140         nodes = Container.elems nl
141         insts = Container.elems il
142         t_ram = truncate . sum . map Node.t_mem $ nodes
143         t_dsk = truncate . sum . map Node.t_dsk $ nodes
144         f_ram = sum . map Node.f_mem $ nodes
145         f_dsk = sum . map Node.f_dsk $ nodes
146     in
147       printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
148                  (length nodes) (length insts)
149                  (length bad_nodes) (length bad_instances)
150                  (t_ram::Integer) f_ram
151                  ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
152                  ccv
153
154
155 -- | Replace slashes with underscore for saving to filesystem
156
157 fixSlash :: String -> String
158 fixSlash = map (\x -> if x == '/' then '_' else x)
159
160 -- | Main function.
161 main :: IO ()
162 main = do
163   cmd_args <- System.getArgs
164   (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
165                       defaultOptions
166
167   let odir = optOutPath opts
168       nlen = maximum . map length $ clusters
169
170   unless (optNoHeader 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"
174
175   mapM_ (\ name ->
176             do
177               printf "%-*s " nlen name
178               hFlush stdout
179               input_data <- Rapi.loadData name
180               let ldresult = input_data >>= Loader.mergeData
181               (case ldresult of
182                  Bad err -> printf "\nError: failed to load data. \
183                                    \Details:\n%s\n" err
184                  Ok x -> do
185                    let (nl, il, csf) = x
186                        (_, fix_nl) = Loader.checkData nl il
187                    putStrLn $ printCluster fix_nl il
188                    when (optShowNodes opts) $ do
189                            putStr $ Cluster.printNodes fix_nl
190                    let ndata = serializeNodes nl csf
191                        idata = serializeInstances nl il csf
192                        oname = odir </> (fixSlash name)
193                    writeFile (oname <.> "nodes") ndata
194                    writeFile (oname <.> "instances") idata)
195        ) clusters