Hscan.hs 4.84 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)
Iustin Pop's avatar
Iustin Pop committed
30
import System (exitWith, ExitCode(..))
Iustin Pop's avatar
Iustin Pop committed
31
32
33
34
35
36
37
38
39
40
import System.IO
import System.FilePath
import qualified System

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
    [ oPrintNodes
    , oOutputDir
54
    , oLuxiSocket
55
56
57
58
    , oVerbose
    , oNoHeaders
    , oShowVer
    , oShowHelp
Iustin Pop's avatar
Iustin Pop committed
59
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 =
Iustin Pop's avatar
Iustin Pop committed
65
66
67
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
        ccv = Cluster.compCV nl
        nodes = Container.elems nl
68
        insts = Container.elems il
69
70
71
72
        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
Iustin Pop's avatar
Iustin Pop committed
73
    in
74
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
75
                 (length nodes) (length insts)
Iustin Pop's avatar
Iustin Pop committed
76
                 (length bad_nodes) (length bad_instances)
77
78
                 t_ram f_ram
                 (t_dsk / 1024) (f_dsk `div` 1024)
Iustin Pop's avatar
Iustin Pop committed
79
80
81
                 ccv


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

86

Iustin Pop's avatar
Iustin Pop committed
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
Iustin Pop's avatar
Iustin Pop committed
92
  return cdata { cdNodes = fix_nl }
93

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

Iustin Pop's avatar
Iustin Pop committed
104
writeData nlen name opts (Ok cdata) = do
105
106
107
108
109
110
  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
111
-- | Inner function for writing cluster data to disk.
112
113
114
115
116
117
118
119
writeDataInner :: Int
               -> String
               -> Options
               -> ClusterData
               -> ClusterData
               -> IO Bool
writeDataInner nlen name opts cdata fixdata = do
  let (ClusterData _ nl il _) = fixdata
Iustin Pop's avatar
Iustin Pop committed
120
  printf "%-*s " nlen name :: IO ()
121
122
123
124
125
126
127
128
  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
129
  writeFile (oname <.> "data") (serializeCluster cdata)
130
  return True
131

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

139
140
141
  let nlen = if null clusters
             then length local
             else maximum . map length $ clusters
Iustin Pop's avatar
Iustin Pop committed
142

143
  unless (optNoHeaders opts) $
Iustin Pop's avatar
Iustin Pop committed
144
145
146
147
         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"

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

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