Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ 78694255

History | View | Annotate | Download (6.5 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
-- | 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