1 {-| Scan clusters via RAPI and write instance/node data files.
5 module Main (main) where
9 import Data.Maybe(fromJust)
13 import System.FilePath
14 import System.Console.GetOpt
15 import qualified System
17 import Text.Printf (printf)
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
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
37 -- | Default values for the command line options.
38 defaultOptions :: Options
39 defaultOptions = Options
40 { optShowNodes = False
48 -- | Options list and functions
49 options :: [OptDescr (Options -> 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}))
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
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
82 printf "%s|%d|%d|%d|%d|%d|%c" name
83 t_mem (Node.n_mem node) (Node.f_mem node)
84 t_dsk (Node.f_dsk node)
85 (if Node.offline node then 'Y' else 'N')
90 -- | Generate instance file data from instance objects
91 serializeInstances :: Cluster.InstanceList -> String
92 -> Cluster.NameList -> Cluster.NameList -> String
93 serializeInstances il csf ktn kti =
94 let etn = map (\(idx, name) -> (idx, name ++ csf)) ktn
95 eti = map (\(idx, name) -> (idx, name ++ csf)) kti
96 instances = Container.elems il
100 iname = fromJust $ lookup (Instance.idx inst) eti
101 pnode = fromJust $ lookup (Instance.pnode inst) etn
102 snode = fromJust $ lookup (Instance.snode inst) etn
104 printf "%s|%d|%d|%s|%s|%s"
105 iname (Instance.mem inst) (Instance.dsk inst)
106 (Instance.run_st inst)
112 -- | Return a one-line summary of cluster state
113 printCluster :: Cluster.NodeList -> Cluster.InstanceList
114 -> Cluster.NameList -> Cluster.NameList
116 printCluster nl il ktn kti =
117 let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
118 ccv = Cluster.compCV nl
119 nodes = Container.elems nl
120 t_ram = truncate . sum . map Node.t_mem $ nodes
121 t_dsk = truncate . sum . map Node.t_dsk $ nodes
122 f_ram = sum . map Node.f_mem $ nodes
123 f_dsk = sum . map Node.f_dsk $ nodes
125 printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
126 (length ktn) (length kti)
127 (length bad_nodes) (length bad_instances)
128 (t_ram::Integer) f_ram
129 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
136 cmd_args <- System.getArgs
137 (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
138 defaultOptions optShowHelp
140 when (optShowVer opts) $ do
141 putStr $ CLI.showVersion "hscan"
144 let odir = optOutPath opts
145 nlen = maximum . map length $ clusters
147 unless (optNoHeader opts) $
148 printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
149 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
150 "t_disk" "f_disk" "Score"
154 printf "%-*s " nlen name
156 node_data <- getNodes name
157 inst_data <- getInstances name
158 (if isLeft(node_data)
159 then putStrLn $ fromLeft node_data
160 else if isLeft(inst_data)
161 then putStrLn $ fromLeft inst_data
163 let ndata = fromRight node_data
164 idata = fromRight inst_data
165 (nl, il, csf, ktn, kti) =
166 Cluster.loadData ndata idata
167 (_, fix_nl) = Cluster.checkData nl il ktn kti
168 putStrLn $ printCluster fix_nl il ktn kti
169 when (optShowNodes opts) $ do
170 putStr $ Cluster.printNodes ktn fix_nl
171 let ndata = serializeNodes nl csf ktn
172 idata = serializeInstances il csf ktn kti
173 oname = odir </> name
174 writeFile (oname <.> "nodes") ndata
175 writeFile (oname <.> "instances") idata)