Remove some extraneous 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.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