hscan.hs 4.66 KB
Newer Older
1 2 3
{-# LANGUAGE CPP #-}

{-| Scan clusters via RAPI or LUXI and write state data files.
Iustin Pop's avatar
Iustin Pop committed
4 5 6

-}

Iustin Pop's avatar
Iustin Pop committed
7 8
{-

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

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.

-}

Iustin Pop's avatar
Iustin Pop committed
28 29
module Main (main) where

30
import Data.Maybe (isJust, fromJust, fromMaybe)
Iustin Pop's avatar
Iustin Pop committed
31
import Monad
Iustin Pop's avatar
Iustin Pop committed
32
import System (exitWith, ExitCode(..))
Iustin Pop's avatar
Iustin Pop committed
33 34 35 36 37 38 39 40 41 42
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
43
#ifndef NO_CURL
Iustin Pop's avatar
Iustin Pop committed
44
import qualified Ganeti.HTools.Rapi as Rapi
45 46
#endif
import qualified Ganeti.HTools.Luxi as Luxi
47
import Ganeti.HTools.Loader (checkData, mergeData, ClusterData(..))
48
import Ganeti.HTools.Text (serializeCluster)
Iustin Pop's avatar
Iustin Pop committed
49

50 51
import Ganeti.HTools.CLI
import Ganeti.HTools.Types
Iustin Pop's avatar
Iustin Pop committed
52 53

-- | Options list and functions
54
options :: [OptType]
Iustin Pop's avatar
Iustin Pop committed
55
options =
56 57
    [ oPrintNodes
    , oOutputDir
58
    , oLuxiSocket
59 60 61 62
    , oVerbose
    , oNoHeaders
    , oShowVer
    , oShowHelp
Iustin Pop's avatar
Iustin Pop committed
63 64 65
    ]

-- | Return a one-line summary of cluster state
66
printCluster :: Node.List -> Instance.List
Iustin Pop's avatar
Iustin Pop committed
67
             -> String
68
printCluster nl il =
Iustin Pop's avatar
Iustin Pop committed
69 70 71
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
        ccv = Cluster.compCV nl
        nodes = Container.elems nl
72
        insts = Container.elems il
73 74 75 76
        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
77
    in
78
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
79
                 (length nodes) (length insts)
Iustin Pop's avatar
Iustin Pop committed
80
                 (length bad_nodes) (length bad_instances)
81 82
                 t_ram f_ram
                 (t_dsk / 1024) (f_dsk `div` 1024)
Iustin Pop's avatar
Iustin Pop committed
83 84 85
                 ccv


86 87 88 89
-- | Replace slashes with underscore for saving to filesystem
fixSlash :: String -> String
fixSlash = map (\x -> if x == '/' then '_' else x)

90

Iustin Pop's avatar
Iustin Pop committed
91 92
-- | Generates serialized data from loader input.
processData :: Result ClusterData -> Result ClusterData
93
processData input_data = do
Iustin Pop's avatar
Iustin Pop committed
94
  cdata@(ClusterData _ nl il _) <- input_data >>= mergeData [] [] []
95
  let (_, fix_nl) = checkData nl il
Iustin Pop's avatar
Iustin Pop committed
96
  return cdata { cdNodes = fix_nl }
97 98 99 100 101

-- | Writes cluster data out
writeData :: Int
          -> String
          -> Options
Iustin Pop's avatar
Iustin Pop committed
102
          -> Result ClusterData
103
          -> IO Bool
104
writeData _ name _ (Bad err) =
105 106
  printf "\nError for %s: failed to load data. Details:\n%s\n" name err >>
  return False
107

Iustin Pop's avatar
Iustin Pop committed
108 109
writeData nlen name opts (Ok cdata) = do
  let (ClusterData _ nl il _) = cdata
Iustin Pop's avatar
Iustin Pop committed
110
  printf "%-*s " nlen name :: IO ()
111 112 113 114 115 116 117 118
  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
119
  writeFile (oname <.> "data") (serializeCluster cdata)
120
  return True
121

Iustin Pop's avatar
Iustin Pop committed
122 123 124 125
-- | Main function.
main :: IO ()
main = do
  cmd_args <- System.getArgs
126
  (opts, clusters) <- parseOpts cmd_args "hscan" options
127
  let local = "LOCAL"
Iustin Pop's avatar
Iustin Pop committed
128

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

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

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

#ifndef NO_CURL
146 147 148 149 150 151
  results <- mapM (\ name ->
                    do
                      input_data <- Rapi.loadData name
                      writeData nlen name opts (processData input_data)
                  ) clusters
  when (not $ all id results) $ exitWith (ExitFailure 2)
152 153 154 155 156
#else
  when (not $ null clusters) $ do
    putStrLn "RAPI/curl backend disabled at compile time, cannot scan clusters"
    exitWith $ ExitFailure 1
#endif