Implement hail allocate (for 2-node requests)
[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 Monad
10 import System
11 import System.IO
12 import System.FilePath
13 import System.Console.GetOpt
14 import qualified System
15
16 import Text.Printf (printf)
17
18 import qualified Ganeti.HTools.Container as Container
19 import qualified Ganeti.HTools.Cluster as Cluster
20 import qualified Ganeti.HTools.Node as Node
21 import qualified Ganeti.HTools.Instance as Instance
22 import qualified Ganeti.HTools.CLI as CLI
23 import qualified Ganeti.HTools.Rapi as Rapi
24 import qualified Ganeti.HTools.Loader as Loader
25 import Ganeti.HTools.Types
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 instance CLI.CLIOptions Options where
38     showVersion = optShowVer
39     showHelp    = optShowHelp
40
41 -- | Default values for the command line options.
42 defaultOptions :: Options
43 defaultOptions  = Options
44  { optShowNodes = False
45  , optOutPath   = "."
46  , optVerbose   = 0
47  , optNoHeader  = False
48  , optShowVer   = False
49  , optShowHelp  = False
50  }
51
52 -- | Options list and functions
53 options :: [OptDescr (Options -> Options)]
54 options =
55     [ Option ['p']     ["print-nodes"]
56       (NoArg (\ opts -> opts { optShowNodes = True }))
57       "print the final node list"
58     , Option ['d']     ["output-dir"]
59       (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
60       "directory in which to write output files"
61     , Option ['v']     ["verbose"]
62       (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
63       "increase the verbosity level"
64     , Option []        ["no-headers"]
65       (NoArg (\ opts -> opts { optNoHeader = True }))
66       "do not show a header line"
67     , Option ['V']     ["version"]
68       (NoArg (\ opts -> opts { optShowVer = True}))
69       "show the version of the program"
70     , Option ['h']     ["help"]
71       (NoArg (\ opts -> opts { optShowHelp = True}))
72       "show help"
73     ]
74
75 -- | Generate node file data from node objects
76 serializeNodes :: Cluster.NodeList -> String -> String
77 serializeNodes nl csf =
78     let nodes = Container.elems nl
79         nlines = map
80                  (\node ->
81                       let name = Node.name node ++ csf
82                           t_mem = (truncate $ Node.t_mem node)::Int
83                           t_dsk = (truncate $ Node.t_dsk node)::Int
84                       in
85                         printf "%s|%d|%d|%d|%d|%d|%c" name
86                                    t_mem (Node.n_mem node) (Node.f_mem node)
87                                    t_dsk (Node.f_dsk node)
88                                    (if Node.offline node then 'Y' else 'N')
89                  )
90                  nodes
91     in unlines nlines
92
93 -- | Generate instance file data from instance objects
94 serializeInstances :: Cluster.NodeList -> Cluster.InstanceList
95                    -> String -> String
96 serializeInstances nl il csf =
97     let instances = Container.elems il
98         nlines = map
99                  (\inst ->
100                       let
101                           iname = Instance.name inst ++ csf
102                           pnode = cNameOf nl $ Instance.pnode inst
103                           snode = cNameOf nl $ Instance.snode inst
104                       in
105                         printf "%s|%d|%d|%s|%s|%s"
106                                iname (Instance.mem inst) (Instance.dsk inst)
107                                (Instance.run_st inst)
108                                pnode snode
109                  )
110                  instances
111     in unlines nlines
112
113 -- | Return a one-line summary of cluster state
114 printCluster :: Cluster.NodeList -> Cluster.InstanceList
115              -> String
116 printCluster nl il =
117     let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
118         ccv = Cluster.compCV nl
119         nodes = Container.elems nl
120         insts = Container.elems il
121         t_ram = truncate . sum . map Node.t_mem $ nodes
122         t_dsk = truncate . sum . map Node.t_dsk $ nodes
123         f_ram = sum . map Node.f_mem $ nodes
124         f_dsk = sum . map Node.f_dsk $ nodes
125     in
126       printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
127                  (length nodes) (length insts)
128                  (length bad_nodes) (length bad_instances)
129                  (t_ram::Integer) f_ram
130                  ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
131                  ccv
132
133
134 -- | Replace slashes with underscore for saving to filesystem
135
136 fixSlash :: String -> String
137 fixSlash = map (\x -> if x == '/' then '_' else x)
138
139 -- | Main function.
140 main :: IO ()
141 main = do
142   cmd_args <- System.getArgs
143   (opts, clusters) <- CLI.parseOpts cmd_args "hscan" options
144                       defaultOptions
145
146   let odir = optOutPath opts
147       nlen = maximum . map length $ clusters
148
149   unless (optNoHeader opts) $
150          printf "%-*s %5s %5s %5s %5s %6s %6s %6s %6s %10s\n" nlen
151                 "Name" "Nodes" "Inst" "BNode" "BInst" "t_mem" "f_mem"
152                 "t_disk" "f_disk" "Score"
153
154   mapM_ (\ name ->
155             do
156               printf "%-*s " nlen name
157               hFlush stdout
158               input_data <- Rapi.loadData name
159               let ldresult = input_data >>= Loader.mergeData
160               (case ldresult of
161                  Bad err -> printf "\nError: failed to load data. \
162                                    \Details:\n%s\n" err
163                  Ok x -> do
164                    let (nl, il, csf) = x
165                        (_, fix_nl) = Loader.checkData nl il
166                    putStrLn $ printCluster fix_nl il
167                    when (optShowNodes opts) $ do
168                            putStr $ Cluster.printNodes fix_nl
169                    let ndata = serializeNodes nl csf
170                        idata = serializeInstances nl il csf
171                        oname = odir </> (fixSlash name)
172                    writeFile (oname <.> "nodes") ndata
173                    writeFile (oname <.> "instances") idata)
174        ) clusters