Hscan.hs 4.67 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, 2012 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, options) 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
import System.Exit
Iustin Pop's avatar
Iustin Pop committed
31
32
33
34
35
36
37
38
39
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
40
import qualified Ganeti.HTools.Rapi as Rapi
41
import qualified Ganeti.HTools.Luxi as Luxi
42
import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
43
import Ganeti.HTools.Text (serializeCluster)
Iustin Pop's avatar
Iustin Pop committed
44

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

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

Iustin Pop's avatar
Iustin Pop committed
60
-- | Return a one-line summary of cluster state.
61
printCluster :: Node.List -> Instance.List
Iustin Pop's avatar
Iustin Pop committed
62
             -> String
63
printCluster nl il =
64
65
66
67
68
69
70
71
72
73
74
75
  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
76

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

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

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

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

Iustin Pop's avatar
Iustin Pop committed
126
-- | Main function.
127
128
main :: Options -> [String] -> IO ()
main opts clusters = do
129
  let local = "LOCAL"
Iustin Pop's avatar
Iustin Pop committed
130

131
132
133
  let nlen = if null clusters
             then length local
             else maximum . map length $ clusters
Iustin Pop's avatar
Iustin Pop committed
134

135
  unless (optNoHeaders opts) $
Iustin Pop's avatar
Iustin Pop committed
136
137
138
139
         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"

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

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