Remove most uses of ktn/kti
[ganeti-local] / hscan.hs
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