Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ 691dcd2a

History | View | Annotate | Download (5.8 kB)

1
{-# LANGUAGE CPP #-}
2

    
3
{-| Scan clusters via RAPI or LUXI and write state data files.
4

    
5
-}
6

    
7
{-
8

    
9
Copyright (C) 2009 Google Inc.
10

    
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation; either version 2 of the License, or
14
(at your option) any later version.
15

    
16
This program is distributed in the hope that it will be useful, but
17
WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
General Public License for more details.
20

    
21
You should have received a copy of the GNU General Public License
22
along with this program; if not, write to the Free Software
23
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
02110-1301, USA.
25

    
26
-}
27

    
28
module Main (main) where
29

    
30
import Data.List
31
import Data.Maybe (isJust, fromJust, fromMaybe)
32
import Monad
33
#ifdef NO_CURL
34
import System (exitWith, ExitCode(..))
35
#endif
36
import System.IO
37
import System.FilePath
38
import qualified System
39

    
40
import Text.Printf (printf)
41

    
42
import qualified Ganeti.HTools.Container as Container
43
import qualified Ganeti.HTools.Cluster as Cluster
44
import qualified Ganeti.HTools.Node as Node
45
import qualified Ganeti.HTools.Instance as Instance
46
#ifndef NO_CURL
47
import qualified Ganeti.HTools.Rapi as Rapi
48
#endif
49
import qualified Ganeti.HTools.Luxi as Luxi
50
import qualified Ganeti.HTools.Loader as Loader
51

    
52
import Ganeti.HTools.CLI
53
import Ganeti.HTools.Types
54

    
55
-- | Options list and functions
56
options :: [OptType]
57
options =
58
    [ oPrintNodes
59
    , oOutputDir
60
    , oLuxiSocket
61
    , oVerbose
62
    , oNoHeaders
63
    , oShowVer
64
    , oShowHelp
65
    ]
66

    
67
-- | Serialize a single node
68
serializeNode :: Node.Node -> String
69
serializeNode node =
70
    printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c" (Node.name node)
71
               (Node.tMem node) (Node.nMem node) (Node.fMem node)
72
               (Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
73
               (if Node.offline node then 'Y' else 'N')
74

    
75
-- | Generate node file data from node objects
76
serializeNodes :: Node.List -> String
77
serializeNodes = unlines . map serializeNode . Container.elems
78

    
79
-- | Serialize a single instance
80
serializeInstance :: Node.List -> Instance.Instance -> String
81
serializeInstance nl inst =
82
    let
83
        iname = Instance.name inst
84
        pnode = Container.nameOf nl (Instance.pNode inst)
85
        sidx = Instance.sNode inst
86
        snode = (if sidx == Node.noSecondary
87
                    then ""
88
                    else Container.nameOf nl sidx)
89
    in
90
      printf "%s|%d|%d|%d|%s|%s|%s|%s"
91
             iname (Instance.mem inst) (Instance.dsk inst)
92
             (Instance.vcpus inst) (Instance.runSt inst)
93
             pnode snode (intercalate "," (Instance.tags inst))
94

    
95
-- | Generate instance file data from instance objects
96
serializeInstances :: Node.List -> Instance.List -> String
97
serializeInstances nl =
98
    unlines . map (serializeInstance nl) . Container.elems
99

    
100
-- | Return a one-line summary of cluster state
101
printCluster :: Node.List -> Instance.List
102
             -> String
103
printCluster nl il =
104
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
105
        ccv = Cluster.compCV nl
106
        nodes = Container.elems nl
107
        insts = Container.elems il
108
        t_ram = sum . map Node.tMem $ nodes
109
        t_dsk = sum . map Node.tDsk $ nodes
110
        f_ram = sum . map Node.fMem $ nodes
111
        f_dsk = sum . map Node.fDsk $ nodes
112
    in
113
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
114
                 (length nodes) (length insts)
115
                 (length bad_nodes) (length bad_instances)
116
                 t_ram f_ram
117
                 (t_dsk / 1024) (f_dsk `div` 1024)
118
                 ccv
119

    
120

    
121
-- | Replace slashes with underscore for saving to filesystem
122
fixSlash :: String -> String
123
fixSlash = map (\x -> if x == '/' then '_' else x)
124

    
125

    
126
-- | Generates serialized data from loader input
127
processData :: Result (Node.AssocList, Instance.AssocList, [String])
128
            -> Result (Node.List, Instance.List, String)
129
processData input_data = do
130
  (nl, il, _) <- input_data >>= Loader.mergeData [] [] []
131
  let (_, fix_nl) = Loader.checkData nl il
132
  let ndata = serializeNodes nl
133
      idata = serializeInstances nl il
134
      adata = ndata ++ ['\n'] ++ idata
135
  return (fix_nl, il, adata)
136

    
137
-- | Writes cluster data out
138
writeData :: Int
139
          -> String
140
          -> Options
141
          -> Result (Node.List, Instance.List, String)
142
          -> IO ()
143
writeData _ name _ (Bad err) =
144
    printf "\nError for %s: failed to load data. Details:\n%s\n" name err
145

    
146
writeData nlen name opts (Ok (nl, il, adata)) = do
147
  printf "%-*s " nlen name :: IO ()
148
  hFlush stdout
149
  let shownodes = optShowNodes opts
150
      odir = optOutPath opts
151
      oname = odir </> fixSlash name
152
  putStrLn $ printCluster nl il
153
  hFlush stdout
154
  when (isJust shownodes) $
155
       putStr $ Cluster.printNodes nl (fromJust shownodes)
156
  writeFile (oname <.> "data") adata
157

    
158

    
159
-- | Main function.
160
main :: IO ()
161
main = do
162
  cmd_args <- System.getArgs
163
  (opts, clusters) <- parseOpts cmd_args "hscan" options
164
  let local = "LOCAL"
165

    
166
  let nlen = if null clusters
167
             then length local
168
             else maximum . map length $ clusters
169

    
170
  unless (optNoHeaders 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
  when (null clusters) $ do
176
         let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
177
         let name = local
178
         input_data <- Luxi.loadData lsock
179
         writeData nlen name opts (processData input_data)
180

    
181
#ifndef NO_CURL
182
  mapM_ (\ name ->
183
            do
184
              input_data <- Rapi.loadData name
185
              writeData nlen name opts (processData input_data)
186
        ) clusters
187
#else
188
  when (not $ null clusters) $ do
189
    putStrLn "RAPI/curl backend disabled at compile time, cannot scan clusters"
190
    exitWith $ ExitFailure 1
191
#endif