Statistics
| Branch: | Tag: | Revision:

root / hscan.hs @ dbd6700b

History | View | Annotate | Download (6.2 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.InstanceList -> String
96
                   -> Cluster.NameList -> String
97
serializeInstances il csf ktn =
98
    let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
99
        instances = Container.elems il
100
        nlines = map
101
                 (\inst ->
102
                      let
103
                          iname = Instance.name inst ++ csf
104
                          pnode = fromJust $ lookup (Instance.pnode inst) etn
105
                          snode = fromJust $ lookup (Instance.snode inst) etn
106
                      in
107
                        printf "%s|%d|%d|%s|%s|%s"
108
                               iname (Instance.mem inst) (Instance.dsk inst)
109
                               (Instance.run_st inst)
110
                               pnode snode
111
                 )
112
                 instances
113
    in unlines nlines
114

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

    
135

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

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

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

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

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

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