Some documentation updates for the new parameters
[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 -- | Serialize a single node
97 serializeNode :: String -> Node.Node -> String
98 serializeNode csf node =
99     let name = Node.name node ++ csf
100         t_mem = (truncate $ Node.t_mem node)::Int
101         t_dsk = (truncate $ Node.t_dsk node)::Int
102     in
103       printf "%s|%d|%d|%d|%d|%d|%c" name
104              t_mem (Node.n_mem node) (Node.f_mem node)
105              t_dsk (Node.f_dsk node)
106              (if Node.offline node then 'Y' else 'N')
107
108 -- | Generate node file data from node objects
109 serializeNodes :: String -> Node.List -> String
110 serializeNodes csf =
111     unlines . map (serializeNode csf) . Container.elems
112
113 -- | Serialize a single instance
114 serializeInstance :: String -> Node.List -> Instance.Instance -> String
115 serializeInstance csf nl inst =
116     let
117         iname = Instance.name inst ++ csf
118         pnode = Container.nameOf nl $ Instance.pnode inst
119         snode = Container.nameOf nl $ Instance.snode inst
120     in
121       printf "%s|%d|%d|%s|%s|%s"
122              iname (Instance.mem inst) (Instance.dsk inst)
123              (Instance.run_st inst)
124              pnode snode
125
126 -- | Generate instance file data from instance objects
127 serializeInstances :: String -> Node.List -> Instance.List -> String
128 serializeInstances csf nl =
129     unlines . map (serializeInstance csf nl) . Container.elems
130
131 -- | Return a one-line summary of cluster state
132 printCluster :: Node.List -> Instance.List
133              -> String
134 printCluster nl il =
135     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
136         ccv = Cluster.compCV nl
137         nodes = Container.elems nl
138         insts = Container.elems il
139         t_ram = sum . map Node.t_mem $ nodes
140         t_dsk = sum . map Node.t_dsk $ nodes
141         f_ram = sum . map Node.f_mem $ nodes
142         f_dsk = sum . map Node.f_dsk $ nodes
143     in
144       printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
145                  (length nodes) (length insts)
146                  (length bad_nodes) (length bad_instances)
147                  t_ram f_ram
148                  (t_dsk / 1024) (f_dsk `div` 1024)
149                  ccv
150
151
152 -- | Replace slashes with underscore for saving to filesystem
153
154 fixSlash :: String -> String
155 fixSlash = map (\x -> if x == '/' then '_' else x)
156
157 -- | Main function.
158 main :: IO ()
159 main = do
160   cmd_args <- System.getArgs
161   (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
162                       defaultOptions
163
164   let odir = optOutPath opts
165       nlen = maximum . map length $ clusters
166
167   unless (optNoHeader opts) $
168          printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
169                 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
170                 "t_disk" "f_disk" "Score"
171
172   mapM_ (\ name ->
173             do
174               printf "%-*s " nlen name
175               hFlush stdout
176               input_data <- Rapi.loadData name
177               let ldresult = input_data >>= Loader.mergeData
178               (case ldresult of
179                  Bad err -> printf "\nError: failed to load data. \
180                                    \Details:\n%s\n" err
181                  Ok x -> do
182                    let (nl, il, csf) = x
183                        (_, fix_nl) = Loader.checkData nl il
184                    putStrLn $ printCluster fix_nl il
185                    when (optShowNodes opts) $ do
186                            putStr $ Cluster.printNodes fix_nl
187                    let ndata = serializeNodes csf nl
188                        idata = serializeInstances csf nl il
189                        oname = odir </> (fixSlash name)
190                    writeFile (oname <.> "nodes") ndata
191                    writeFile (oname <.> "instances") idata)
192        ) clusters