Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ 9f6dcdea

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
    printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c" (Node.name node ++ csf)
100
               (Node.t_mem node) (Node.n_mem node) (Node.f_mem node)
101
               (Node.t_dsk node) (Node.f_dsk node) (Node.t_cpu node)
102
               (if Node.offline node then 'Y' else 'N')
103

    
104
-- | Generate node file data from node objects
105
serializeNodes :: String -> Node.List -> String
106
serializeNodes csf =
107
    unlines . map (serializeNode csf) . Container.elems
108

    
109
-- | Serialize a single instance
110
serializeInstance :: String -> Node.List -> Instance.Instance -> String
111
serializeInstance csf nl inst =
112
    let
113
        iname = Instance.name inst ++ csf
114
        pnode = Container.nameOf nl (Instance.pnode inst) ++ csf
115
        sidx = Instance.snode inst
116
        snode = (if sidx == Node.noSecondary
117
                    then ""
118
                    else Container.nameOf nl sidx ++ csf)
119
    in
120
      printf "%s|%d|%d|%d|%s|%s|%s"
121
             iname (Instance.mem inst) (Instance.dsk inst)
122
             (Instance.vcpus inst) (Instance.run_st inst)
123
             pnode snode
124

    
125
-- | Generate instance file data from instance objects
126
serializeInstances :: String -> Node.List -> Instance.List -> String
127
serializeInstances csf nl =
128
    unlines . map (serializeInstance csf nl) . Container.elems
129

    
130
-- | Return a one-line summary of cluster state
131
printCluster :: Node.List -> Instance.List
132
             -> String
133
printCluster nl il =
134
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
135
        ccv = Cluster.compCV nl
136
        nodes = Container.elems nl
137
        insts = Container.elems il
138
        t_ram = sum . map Node.t_mem $ nodes
139
        t_dsk = sum . map Node.t_dsk $ nodes
140
        f_ram = sum . map Node.f_mem $ nodes
141
        f_dsk = sum . map Node.f_dsk $ nodes
142
    in
143
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
144
                 (length nodes) (length insts)
145
                 (length bad_nodes) (length bad_instances)
146
                 t_ram f_ram
147
                 (t_dsk / 1024) (f_dsk `div` 1024)
148
                 ccv
149

    
150

    
151
-- | Replace slashes with underscore for saving to filesystem
152

    
153
fixSlash :: String -> String
154
fixSlash = map (\x -> if x == '/' then '_' else x)
155

    
156
-- | Main function.
157
main :: IO ()
158
main = do
159
  cmd_args <- System.getArgs
160
  (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
161
                      defaultOptions
162

    
163
  let odir = optOutPath opts
164
      nlen = maximum . map length $ clusters
165

    
166
  unless (optNoHeader opts) $
167
         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
168
                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
169
                "t_disk" "f_disk" "Score"
170

    
171
  mapM_ (\ name ->
172
            do
173
              printf "%-*s " nlen name
174
              hFlush stdout
175
              input_data <- Rapi.loadData name
176
              let ldresult = input_data >>= Loader.mergeData
177
              (case ldresult of
178
                 Bad err -> printf "\nError: failed to load data. \
179
                                   \Details:\n%s\n" err
180
                 Ok x -> do
181
                   let (nl, il, csf) = x
182
                       (_, fix_nl) = Loader.checkData nl il
183
                   putStrLn $ printCluster fix_nl il
184
                   when (optShowNodes opts) $
185
                        putStr $ Cluster.printNodes fix_nl
186
                   let ndata = serializeNodes csf nl
187
                       idata = serializeInstances csf nl il
188
                       oname = odir </> fixSlash name
189
                   writeFile (oname <.> "nodes") ndata
190
                   writeFile (oname <.> "instances") idata)
191
       ) clusters