Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ e2fa2baf

History | View | Annotate | Download (6.8 kB)

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