8a3a64e489d6b47ac84392771391611cf51b1bc8
[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 Ganeti.HTools.Rapi
25 import Ganeti.HTools.Utils
26
27 -- | Command line options structure.
28 data Options = Options
29     { optShowNodes :: Bool     -- ^ Whether to show node status
30     , optOutPath   :: FilePath -- ^ Path to the output directory
31     , optVerbose   :: Int      -- ^ Verbosity level
32     , optNoHeader  :: Bool     -- ^ Do not show a header line
33     , optShowVer   :: Bool     -- ^ Just show the program version
34     , optShowHelp  :: Bool     -- ^ Just show the help
35     } deriving Show
36
37 -- | Default values for the command line options.
38 defaultOptions :: Options
39 defaultOptions  = Options
40  { optShowNodes = False
41  , optOutPath   = "."
42  , optVerbose   = 0
43  , optNoHeader  = False
44  , optShowVer   = False
45  , optShowHelp  = False
46  }
47
48 -- | Options list and functions
49 options :: [OptDescr (Options -> Options)]
50 options =
51     [ Option ['p']     ["print-nodes"]
52       (NoArg (\ opts -> opts { optShowNodes = True }))
53       "print the final node list"
54     , Option ['d']     ["output-dir"]
55       (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
56       "directory in which to write output files"
57     , Option ['v']     ["verbose"]
58       (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
59       "increase the verbosity level"
60     , Option []        ["no-headers"]
61       (NoArg (\ opts -> opts { optNoHeader = True }))
62       "do not show a header line"
63     , Option ['V']     ["version"]
64       (NoArg (\ opts -> opts { optShowVer = True}))
65       "show the version of the program"
66     , Option ['h']     ["help"]
67       (NoArg (\ opts -> opts { optShowHelp = True}))
68       "show help"
69     ]
70
71 -- | Generate node file data from node objects
72 serializeNodes :: Cluster.NodeList -> String -> Cluster.NameList -> String
73 serializeNodes nl csf ktn =
74     let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
75         nodes = Container.elems nl
76         nlines = map
77                  (\node ->
78                       let name = (fromJust $ lookup (Node.idx node) etn)
79                           t_mem = (truncate $ Node.t_mem node)::Int
80                           t_dsk = (truncate $ Node.t_dsk node)::Int
81                       in
82                         printf "%s|%d|%d|%d|%d|%d" name
83                                    t_mem (Node.n_mem node) (Node.f_mem node)
84                                    t_dsk (Node.f_dsk node))
85                  nodes
86     in unlines nlines
87
88 -- | Generate instance file data from instance objects
89 serializeInstances :: Cluster.InstanceList -> String
90                    -> Cluster.NameList -> Cluster.NameList -> String
91 serializeInstances il csf ktn kti =
92     let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
93         eti = map (\(idx, name) -> (idx, name ++ csf)) kti
94         instances = Container.elems il
95         nlines = map
96                  (\inst ->
97                       let
98                           iname = fromJust $ lookup (Instance.idx inst) eti
99                           pnode = fromJust $ lookup (Instance.pnode inst) etn
100                           snode = fromJust $ lookup (Instance.snode inst) etn
101                       in
102                         printf "%s|%d|%d|%s|%s"
103                                iname (Instance.mem inst) (Instance.dsk inst)
104                                pnode snode
105                  )
106                  instances
107     in unlines nlines
108
109 -- | Return a one-line summary of cluster state
110 printCluster :: Cluster.NodeList -> Cluster.InstanceList
111              -> Cluster.NameList -> Cluster.NameList
112              -> String
113 printCluster nl il ktn kti =
114     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
115         ccv = Cluster.compCV nl
116         nodes = Container.elems nl
117         t_ram = truncate . sum . map Node.t_mem $ nodes
118         t_dsk = truncate . sum . map Node.t_dsk $ nodes
119         f_ram = sum . map Node.f_mem $ nodes
120         f_dsk = sum . map Node.f_dsk $ nodes
121     in
122       printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
123                  (length ktn) (length kti)
124                  (length bad_nodes) (length bad_instances)
125                  (t_ram::Integer) f_ram
126                  ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
127                  ccv
128
129
130 -- | Main function.
131 main :: IO ()
132 main = do
133   cmd_args <- System.getArgs
134   (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
135                       defaultOptions optShowHelp
136
137   when (optShowVer opts) $ do
138          putStr $ CLI.showVersion "hscan"
139          exitWith ExitSuccess
140
141   let odir = optOutPath opts
142       nlen = maximum . map length $ clusters
143
144   unless (optNoHeader opts) $
145          printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
146                 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
147                 "t_disk" "f_disk" "Score"
148
149   mapM (\ name ->
150             do
151               printf "%-*s " nlen name
152               hFlush stdout
153               node_data <- getNodes name
154               inst_data <- getInstances name
155               (if isLeft(node_data)
156                then putStrLn $ fromLeft node_data
157                else if isLeft(inst_data)
158                     then putStrLn $ fromLeft inst_data
159                     else do
160                       let ndata = fromRight node_data
161                           idata = fromRight inst_data
162                           (nl, il, csf, ktn, kti) =
163                               Cluster.loadData ndata idata
164                       putStrLn $ printCluster nl il ktn kti
165                       when (optShowNodes opts) $
166                            putStr $ Cluster.printNodes ktn nl
167                       let ndata = serializeNodes nl csf ktn
168                           idata = serializeInstances il csf ktn kti
169                           oname = odir </> name
170                       writeFile (oname <.> "nodes") ndata
171                       writeFile (oname <.> "instances") idata)
172        ) clusters
173   exitWith ExitSuccess