Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ db1bcfe8

History | View | Annotate | Download (6.1 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 -> String
78
serializeNodes nl csf =
79
    let nodes = Container.elems nl
80
        nlines = map
81
                 (\node ->
82
                      let name = Node.name node ++ csf
83
                          t_mem = (truncate $ Node.t_mem node)::Int
84
                          t_dsk = (truncate $ Node.t_dsk node)::Int
85
                      in
86
                        printf "%s|%d|%d|%d|%d|%d|%c" name
87
                                   t_mem (Node.n_mem node) (Node.f_mem node)
88
                                   t_dsk (Node.f_dsk node)
89
                                   (if Node.offline node then 'Y' else 'N')
90
                 )
91
                 nodes
92
    in unlines nlines
93

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

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

    
134

    
135
-- | Replace slashes with underscore for saving to filesystem
136

    
137
fixSlash :: String -> String
138
fixSlash = map (\x -> if x == '/' then '_' else x)
139

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

    
147
  let odir = optOutPath opts
148
      nlen = maximum . map length $ clusters
149

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

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