Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ 75d1edf8

History | View | Annotate | Download (6.5 kB)

1
{-| Scan clusters via RAPI and write instance/node data files.
2

    
3
-}
4

    
5
module Main (main) where
6

    
7
import Data.List
8
import Data.Function
9
import Data.Maybe(fromJust)
10
import Monad
11
import System
12
import System.IO
13
import System.FilePath
14
import System.Console.GetOpt
15
import qualified System
16

    
17
import Text.Printf (printf)
18

    
19
import qualified Ganeti.HTools.Container as Container
20
import qualified Ganeti.HTools.Cluster as Cluster
21
import qualified Ganeti.HTools.Node as Node
22
import qualified Ganeti.HTools.Instance as Instance
23
import qualified Ganeti.HTools.CLI as CLI
24
import qualified Ganeti.HTools.Rapi as Rapi
25
import qualified Ganeti.HTools.Loader as Loader
26
import Ganeti.HTools.Types
27

    
28
-- | Command line options structure.
29
data Options = Options
30
    { optShowNodes :: Bool     -- ^ Whether to show node status
31
    , optOutPath   :: FilePath -- ^ Path to the output directory
32
    , optVerbose   :: Int      -- ^ Verbosity level
33
    , optNoHeader  :: Bool     -- ^ Do not show a header line
34
    , optShowVer   :: Bool     -- ^ Just show the program version
35
    , optShowHelp  :: Bool     -- ^ Just show the help
36
    } deriving Show
37

    
38
instance CLI.CLIOptions Options where
39
    showVersion = optShowVer
40
    showHelp    = optShowHelp
41

    
42
-- | Default values for the command line options.
43
defaultOptions :: Options
44
defaultOptions  = Options
45
 { optShowNodes = False
46
 , optOutPath   = "."
47
 , optVerbose   = 0
48
 , optNoHeader  = False
49
 , optShowVer   = False
50
 , optShowHelp  = False
51
 }
52

    
53
-- | Options list and functions
54
options :: [OptDescr (Options -> Options)]
55
options =
56
    [ Option ['p']     ["print-nodes"]
57
      (NoArg (\ opts -> opts { optShowNodes = True }))
58
      "print the final node list"
59
    , Option ['d']     ["output-dir"]
60
      (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
61
      "directory in which to write output files"
62
    , Option ['v']     ["verbose"]
63
      (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
64
      "increase the verbosity level"
65
    , Option []        ["no-headers"]
66
      (NoArg (\ opts -> opts { optNoHeader = True }))
67
      "do not show a header line"
68
    , Option ['V']     ["version"]
69
      (NoArg (\ opts -> opts { optShowVer = True}))
70
      "show the version of the program"
71
    , Option ['h']     ["help"]
72
      (NoArg (\ opts -> opts { optShowHelp = True}))
73
      "show help"
74
    ]
75

    
76
-- | Generate node file data from node objects
77
serializeNodes :: Cluster.NodeList -> String -> Cluster.NameList -> String
78
serializeNodes nl csf ktn =
79
    let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
80
        nodes = Container.elems nl
81
        nlines = map
82
                 (\node ->
83
                      let name = (fromJust $ lookup (Node.idx node) etn)
84
                          t_mem = (truncate $ Node.t_mem node)::Int
85
                          t_dsk = (truncate $ Node.t_dsk node)::Int
86
                      in
87
                        printf "%s|%d|%d|%d|%d|%d|%c" name
88
                                   t_mem (Node.n_mem node) (Node.f_mem node)
89
                                   t_dsk (Node.f_dsk node)
90
                                   (if Node.offline node then 'Y' else 'N')
91
                 )
92
                 nodes
93
    in unlines nlines
94

    
95
-- | Generate instance file data from instance objects
96
serializeInstances :: Cluster.InstanceList -> String
97
                   -> Cluster.NameList -> Cluster.NameList -> String
98
serializeInstances il csf ktn kti =
99
    let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
100
        eti = map (\(idx, name) -> (idx, name ++ csf)) kti
101
        instances = Container.elems il
102
        nlines = map
103
                 (\inst ->
104
                      let
105
                          iname = fromJust $ lookup (Instance.idx inst) eti
106
                          pnode = fromJust $ lookup (Instance.pnode inst) etn
107
                          snode = fromJust $ lookup (Instance.snode inst) etn
108
                      in
109
                        printf "%s|%d|%d|%s|%s|%s"
110
                               iname (Instance.mem inst) (Instance.dsk inst)
111
                               (Instance.run_st inst)
112
                               pnode snode
113
                 )
114
                 instances
115
    in unlines nlines
116

    
117
-- | Return a one-line summary of cluster state
118
printCluster :: Cluster.NodeList -> Cluster.InstanceList
119
             -> Cluster.NameList -> Cluster.NameList
120
             -> String
121
printCluster nl il ktn kti =
122
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
123
        ccv = Cluster.compCV nl
124
        nodes = Container.elems nl
125
        t_ram = truncate . sum . map Node.t_mem $ nodes
126
        t_dsk = truncate . sum . map Node.t_dsk $ nodes
127
        f_ram = sum . map Node.f_mem $ nodes
128
        f_dsk = sum . map Node.f_dsk $ nodes
129
    in
130
      printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
131
                 (length ktn) (length kti)
132
                 (length bad_nodes) (length bad_instances)
133
                 (t_ram::Integer) f_ram
134
                 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
135
                 ccv
136

    
137

    
138
-- | Replace slashes with underscore for saving to filesystem
139

    
140
fixSlash :: String -> String
141
fixSlash = map (\x -> if x == '/' then '_' else x)
142

    
143
-- | Main function.
144
main :: IO ()
145
main = do
146
  cmd_args <- System.getArgs
147
  (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
148
                      defaultOptions
149

    
150
  let odir = optOutPath opts
151
      nlen = maximum . map length $ clusters
152

    
153
  unless (optNoHeader opts) $
154
         printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
155
                "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
156
                "t_disk" "f_disk" "Score"
157

    
158
  mapM_ (\ name ->
159
            do
160
              printf "%-*s " nlen name
161
              hFlush stdout
162
              input_data <- Rapi.loadData name
163
              let ldresult = input_data >>= Loader.mergeData
164
              (case ldresult of
165
                 Bad err -> printf "\nError: failed to load data. \
166
                                   \Details:\n%s\n" err
167
                 Ok x -> do
168
                   let (nl, il, csf, ktn, kti) = x
169
                       (_, fix_nl) = Cluster.checkData nl il ktn kti
170
                   putStrLn $ printCluster fix_nl il ktn kti
171
                   when (optShowNodes opts) $ do
172
                           putStr $ Cluster.printNodes ktn fix_nl
173
                   let ndata = serializeNodes nl csf ktn
174
                       idata = serializeInstances il csf ktn kti
175
                       oname = odir </> (fixSlash name)
176
                   writeFile (oname <.> "nodes") ndata
177
                   writeFile (oname <.> "instances") idata)
178
       ) clusters