Revision 7b695a0d

b/.gitignore
98 98
/htools/.hpc
99 99
/htools/coverage
100 100

  
101
/htools/hscan
102 101
/htools/hspace
103 102
/htools/htools
104 103
/htools/test
b/Makefile.am
307 307
	doc/walkthrough.rst
308 308

  
309 309
HS_PROGS = \
310
	htools/hscan \
311 310
	htools/hspace \
312 311
	htools/htools
313
HS_BIN_ROLES = hbal
312
HS_BIN_ROLES = hbal hscan
314 313

  
315 314
HS_ALL_PROGS = $(HS_PROGS) htools/test
316 315
HS_PROG_SRCS = $(patsubst %,%.hs,$(HS_ALL_PROGS))
......
344 343
	htools/Ganeti/HTools/Utils.hs \
345 344
	htools/Ganeti/HTools/Program/Hail.hs \
346 345
	htools/Ganeti/HTools/Program/Hbal.hs \
346
	htools/Ganeti/HTools/Program/Hscan.hs \
347 347
	htools/Ganeti/Jobs.hs \
348 348
	htools/Ganeti/Luxi.hs \
349 349
	htools/Ganeti/OpCodes.hs
b/htools/Ganeti/HTools/Program/Hscan.hs
1
{-| Scan clusters via RAPI or LUXI and write state data files.
2

  
3
-}
4

  
5
{-
6

  
7
Copyright (C) 2009, 2010, 2011 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 Ganeti.HTools.Program.Hscan (main) where
27

  
28
import Control.Monad
29
import Data.Maybe (isJust, fromJust, fromMaybe)
30
import System (exitWith, ExitCode(..))
31
import System.IO
32
import System.FilePath
33
import qualified System
34

  
35
import Text.Printf (printf)
36

  
37
import qualified Ganeti.HTools.Container as Container
38
import qualified Ganeti.HTools.Cluster as Cluster
39
import qualified Ganeti.HTools.Node as Node
40
import qualified Ganeti.HTools.Instance as Instance
41
import qualified Ganeti.HTools.Rapi as Rapi
42
import qualified Ganeti.HTools.Luxi as Luxi
43
import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
44
import Ganeti.HTools.Text (serializeCluster)
45

  
46
import Ganeti.HTools.CLI
47
import Ganeti.HTools.Types
48

  
49
-- | Options list and functions
50
options :: [OptType]
51
options =
52
    [ oPrintNodes
53
    , oOutputDir
54
    , oLuxiSocket
55
    , oVerbose
56
    , oNoHeaders
57
    , oShowVer
58
    , oShowHelp
59
    ]
60

  
61
-- | Return a one-line summary of cluster state
62
printCluster :: Node.List -> Instance.List
63
             -> String
64
printCluster nl il =
65
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
66
        ccv = Cluster.compCV nl
67
        nodes = Container.elems nl
68
        insts = Container.elems il
69
        t_ram = sum . map Node.tMem $ nodes
70
        t_dsk = sum . map Node.tDsk $ nodes
71
        f_ram = sum . map Node.fMem $ nodes
72
        f_dsk = sum . map Node.fDsk $ nodes
73
    in
74
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
75
                 (length nodes) (length insts)
76
                 (length bad_nodes) (length bad_instances)
77
                 t_ram f_ram
78
                 (t_dsk / 1024) (f_dsk `div` 1024)
79
                 ccv
80

  
81

  
82
-- | Replace slashes with underscore for saving to filesystem
83
fixSlash :: String -> String
84
fixSlash = map (\x -> if x == '/' then '_' else x)
85

  
86

  
87
-- | Generates serialized data from loader input.
88
processData :: ClusterData -> Result ClusterData
89
processData input_data = do
90
  cdata@(ClusterData _ nl il _) <- mergeData [] [] [] [] input_data
91
  let (_, fix_nl) = checkData nl il
92
  return cdata { cdNodes = fix_nl }
93

  
94
-- | Writes cluster data out
95
writeData :: Int
96
          -> String
97
          -> Options
98
          -> Result ClusterData
99
          -> IO Bool
100
writeData _ name _ (Bad err) =
101
  printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
102
  return False
103

  
104
writeData nlen name opts (Ok cdata) = do
105
  let fixdata = processData cdata
106
  case fixdata of
107
    Bad err -> printf "\nError for %s: failed to process data. Details:\n%s\n"
108
               name err >> return False
109
    Ok processed -> writeDataInner nlen name opts cdata processed
110

  
111
writeDataInner :: Int
112
               -> String
113
               -> Options
114
               -> ClusterData
115
               -> ClusterData
116
               -> IO Bool
117
writeDataInner nlen name opts cdata fixdata = do
118
  let (ClusterData _ nl il _) = fixdata
119
  printf "%-*s " nlen name :: IO ()
120
  hFlush stdout
121
  let shownodes = optShowNodes opts
122
      odir = optOutPath opts
123
      oname = odir </> fixSlash name
124
  putStrLn $ printCluster nl il
125
  hFlush stdout
126
  when (isJust shownodes) $
127
       putStr $ Cluster.printNodes nl (fromJust shownodes)
128
  writeFile (oname <.> "data") (serializeCluster cdata)
129
  return True
130

  
131
-- | Main function.
132
main :: IO ()
133
main = do
134
  cmd_args <- System.getArgs
135
  (opts, clusters) <- parseOpts cmd_args "hscan" options
136
  let local = "LOCAL"
137

  
138
  let nlen = if null clusters
139
             then length local
140
             else maximum . map length $ clusters
141

  
142
  unless (optNoHeaders opts) $
143
         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
144
                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
145
                "t_disk" "f_disk" "Score"
146

  
147
  when (null clusters) $ do
148
         let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
149
         let name = local
150
         input_data <- Luxi.loadData lsock
151
         result <- writeData nlen name opts input_data
152
         unless result $ exitWith $ ExitFailure 2
153

  
154
  results <- mapM (\name -> Rapi.loadData name >>= writeData nlen name opts)
155
             clusters
156
  unless (all id results) $ exitWith (ExitFailure 2)
/dev/null
1
{-| Scan clusters via RAPI or LUXI and write state data files.
2

  
3
-}
4

  
5
{-
6

  
7
Copyright (C) 2009, 2010, 2011 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 Control.Monad
29
import Data.Maybe (isJust, fromJust, fromMaybe)
30
import System (exitWith, ExitCode(..))
31
import System.IO
32
import System.FilePath
33
import qualified System
34

  
35
import Text.Printf (printf)
36

  
37
import qualified Ganeti.HTools.Container as Container
38
import qualified Ganeti.HTools.Cluster as Cluster
39
import qualified Ganeti.HTools.Node as Node
40
import qualified Ganeti.HTools.Instance as Instance
41
import qualified Ganeti.HTools.Rapi as Rapi
42
import qualified Ganeti.HTools.Luxi as Luxi
43
import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
44
import Ganeti.HTools.Text (serializeCluster)
45

  
46
import Ganeti.HTools.CLI
47
import Ganeti.HTools.Types
48

  
49
-- | Options list and functions
50
options :: [OptType]
51
options =
52
    [ oPrintNodes
53
    , oOutputDir
54
    , oLuxiSocket
55
    , oVerbose
56
    , oNoHeaders
57
    , oShowVer
58
    , oShowHelp
59
    ]
60

  
61
-- | Return a one-line summary of cluster state
62
printCluster :: Node.List -> Instance.List
63
             -> String
64
printCluster nl il =
65
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
66
        ccv = Cluster.compCV nl
67
        nodes = Container.elems nl
68
        insts = Container.elems il
69
        t_ram = sum . map Node.tMem $ nodes
70
        t_dsk = sum . map Node.tDsk $ nodes
71
        f_ram = sum . map Node.fMem $ nodes
72
        f_dsk = sum . map Node.fDsk $ nodes
73
    in
74
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
75
                 (length nodes) (length insts)
76
                 (length bad_nodes) (length bad_instances)
77
                 t_ram f_ram
78
                 (t_dsk / 1024) (f_dsk `div` 1024)
79
                 ccv
80

  
81

  
82
-- | Replace slashes with underscore for saving to filesystem
83
fixSlash :: String -> String
84
fixSlash = map (\x -> if x == '/' then '_' else x)
85

  
86

  
87
-- | Generates serialized data from loader input.
88
processData :: ClusterData -> Result ClusterData
89
processData input_data = do
90
  cdata@(ClusterData _ nl il _) <- mergeData [] [] [] [] input_data
91
  let (_, fix_nl) = checkData nl il
92
  return cdata { cdNodes = fix_nl }
93

  
94
-- | Writes cluster data out
95
writeData :: Int
96
          -> String
97
          -> Options
98
          -> Result ClusterData
99
          -> IO Bool
100
writeData _ name _ (Bad err) =
101
  printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
102
  return False
103

  
104
writeData nlen name opts (Ok cdata) = do
105
  let fixdata = processData cdata
106
  case fixdata of
107
    Bad err -> printf "\nError for %s: failed to process data. Details:\n%s\n"
108
               name err >> return False
109
    Ok processed -> writeDataInner nlen name opts cdata processed
110

  
111
writeDataInner :: Int
112
               -> String
113
               -> Options
114
               -> ClusterData
115
               -> ClusterData
116
               -> IO Bool
117
writeDataInner nlen name opts cdata fixdata = do
118
  let (ClusterData _ nl il _) = fixdata
119
  printf "%-*s " nlen name :: IO ()
120
  hFlush stdout
121
  let shownodes = optShowNodes opts
122
      odir = optOutPath opts
123
      oname = odir </> fixSlash name
124
  putStrLn $ printCluster nl il
125
  hFlush stdout
126
  when (isJust shownodes) $
127
       putStr $ Cluster.printNodes nl (fromJust shownodes)
128
  writeFile (oname <.> "data") (serializeCluster cdata)
129
  return True
130

  
131
-- | Main function.
132
main :: IO ()
133
main = do
134
  cmd_args <- System.getArgs
135
  (opts, clusters) <- parseOpts cmd_args "hscan" options
136
  let local = "LOCAL"
137

  
138
  let nlen = if null clusters
139
             then length local
140
             else maximum . map length $ clusters
141

  
142
  unless (optNoHeaders opts) $
143
         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
144
                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
145
                "t_disk" "f_disk" "Score"
146

  
147
  when (null clusters) $ do
148
         let lsock = fromMaybe defaultLuxiSocket (optLuxi opts)
149
         let name = local
150
         input_data <- Luxi.loadData lsock
151
         result <- writeData nlen name opts input_data
152
         unless result $ exitWith $ ExitFailure 2
153

  
154
  results <- mapM (\name -> Rapi.loadData name >>= writeData nlen name opts)
155
             clusters
156
  unless (all id results) $ exitWith (ExitFailure 2)
b/htools/htools.hs
32 32
import Ganeti.HTools.Utils
33 33
import qualified Ganeti.HTools.Program.Hail as Hail
34 34
import qualified Ganeti.HTools.Program.Hbal as Hbal
35
import qualified Ganeti.HTools.Program.Hscan as Hscan
35 36

  
36 37
-- | Supported binaries.
37 38
personalities :: [(String, IO ())]
38 39
personalities = [ ("hail", Hail.main)
39 40
                , ("hbal", Hbal.main)
41
                , ("hscan", Hscan.main)
40 42
                ]
41 43

  
42 44
-- | Display usage and exit.

Also available in: Unified diff