Hscan.hs 4.73 KB
Newer Older
1
{-| Scan clusters via RAPI or LUXI and write state data files.
Iustin Pop's avatar
Iustin Pop committed
2
3
4

-}

Iustin Pop's avatar
Iustin Pop committed
5
6
{-

7
Copyright (C) 2009, 2010, 2011 Google Inc.
Iustin Pop's avatar
Iustin Pop committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

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

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

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

-}

26
module Ganeti.HTools.Program.Hscan (main) where
Iustin Pop's avatar
Iustin Pop committed
27

Iustin Pop's avatar
Iustin Pop committed
28
import Control.Monad
29
import Data.Maybe (isJust, fromJust, fromMaybe)
30
31
import System.Environment (getArgs)
import System.Exit
Iustin Pop's avatar
Iustin Pop committed
32
33
34
35
36
37
38
39
40
import System.IO
import System.FilePath

import Text.Printf (printf)

import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Cluster as Cluster
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Instance as Instance
Iustin Pop's avatar
Iustin Pop committed
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)
Iustin Pop's avatar
Iustin Pop committed
45

46
47
import Ganeti.HTools.CLI
import Ganeti.HTools.Types
Iustin Pop's avatar
Iustin Pop committed
48

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

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

Iustin Pop's avatar
Iustin Pop committed
78
-- | Replace slashes with underscore for saving to filesystem.
79
80
81
fixSlash :: String -> String
fixSlash = map (\x -> if x == '/' then '_' else x)

Iustin Pop's avatar
Iustin Pop committed
82
-- | Generates serialized data from loader input.
83
processData :: ClusterData -> Result ClusterData
84
processData input_data = do
85
  cdata@(ClusterData _ nl il _ _) <- mergeData [] [] [] [] input_data
86
  let (_, fix_nl) = checkData nl il
Iustin Pop's avatar
Iustin Pop committed
87
  return cdata { cdNodes = fix_nl }
88

Iustin Pop's avatar
Iustin Pop committed
89
-- | Writes cluster data out.
90
91
92
writeData :: Int
          -> String
          -> Options
Iustin Pop's avatar
Iustin Pop committed
93
          -> Result ClusterData
94
          -> IO Bool
95
writeData _ name _ (Bad err) =
96
97
  printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
  return False
98

Iustin Pop's avatar
Iustin Pop committed
99
writeData nlen name opts (Ok cdata) = do
100
101
102
103
104
105
  let fixdata = processData cdata
  case fixdata of
    Bad err -> printf "\nError for %s: failed to process data. Details:\n%s\n"
               name err >> return False
    Ok processed -> writeDataInner nlen name opts cdata processed

Iustin Pop's avatar
Iustin Pop committed
106
-- | Inner function for writing cluster data to disk.
107
108
109
110
111
112
113
writeDataInner :: Int
               -> String
               -> Options
               -> ClusterData
               -> ClusterData
               -> IO Bool
writeDataInner nlen name opts cdata fixdata = do
114
  let (ClusterData _ nl il _ _) = fixdata
Iustin Pop's avatar
Iustin Pop committed
115
  printf "%-*s " nlen name :: IO ()
116
117
118
119
120
121
122
123
  hFlush stdout
  let shownodes = optShowNodes opts
      odir = optOutPath opts
      oname = odir </> fixSlash name
  putStrLn $ printCluster nl il
  hFlush stdout
  when (isJust shownodes) $
       putStr $ Cluster.printNodes nl (fromJust shownodes)
Iustin Pop's avatar
Iustin Pop committed
124
  writeFile (oname <.> "data") (serializeCluster cdata)
125
  return True
126

Iustin Pop's avatar
Iustin Pop committed
127
128
129
-- | Main function.
main :: IO ()
main = do
130
  cmd_args <- getArgs
131
  (opts, clusters) <- parseOpts cmd_args "hscan" options
132
  let local = "LOCAL"
Iustin Pop's avatar
Iustin Pop committed
133

134
135
136
  let nlen = if null clusters
             then length local
             else maximum . map length $ clusters
Iustin Pop's avatar
Iustin Pop committed
137

138
  unless (optNoHeaders opts) $
Iustin Pop's avatar
Iustin Pop committed
139
140
141
142
         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
                "t_disk" "f_disk" "Score"

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

150
151
152
  results <- mapM (\name -> Rapi.loadData name >>= writeData nlen name opts)
             clusters
  unless (all id results) $ exitWith (ExitFailure 2)