Rename Ganeti/HTools/Utils.hs to Ganeti/Utils.hs
[ganeti-local] / htools / Ganeti / HTools / Program / Hinfo.hs
1 {-| Cluster information printer.
2
3 -}
4
5 {-
6
7 Copyright (C) 2012 Google Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 02110-1301, USA.
23
24 -}
25
26 module Ganeti.HTools.Program.Hinfo (main, options) where
27
28 import Control.Monad
29 import Data.List
30 import System.Exit
31 import System.IO
32
33 import Text.Printf (printf)
34
35 import qualified Ganeti.HTools.Container as Container
36 import qualified Ganeti.HTools.Cluster as Cluster
37 import qualified Ganeti.HTools.Node as Node
38 import qualified Ganeti.HTools.Group as Group
39 import qualified Ganeti.HTools.Instance as Instance
40
41 import Ganeti.HTools.CLI
42 import Ganeti.HTools.ExtLoader
43 import Ganeti.HTools.Loader
44 import Ganeti.Utils
45
46 -- | Options list and functions.
47 options :: [OptType]
48 options =
49   [ oPrintNodes
50   , oPrintInsts
51   , oDataFile
52   , oRapiMaster
53   , oLuxiSocket
54   , oIAllocSrc
55   , oVerbose
56   , oQuiet
57   , oOfflineNode
58   ]
59
60 -- | Group information data-type.
61 data GroupInfo = GroupInfo { giName      :: String
62                            , giNodeCount :: Int
63                            , giInstCount :: Int
64                            , giBadNodes  :: Int
65                            , giBadInsts  :: Int
66                            , giN1Status  :: Bool
67                            , giScore     :: Double
68                            }
69
70 -- | Node group statistics.
71 calcGroupInfo :: Group.Group
72               -> Node.List
73               -> Instance.List
74               -> GroupInfo
75 calcGroupInfo g nl il =
76   let nl_size                    = Container.size nl
77       il_size                    = Container.size il
78       (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
79       bn_size                    = length bad_nodes
80       bi_size                    = length bad_instances
81       n1h                        = bn_size == 0
82       score                      = Cluster.compCV nl
83   in GroupInfo (Group.name g) nl_size il_size bn_size bi_size n1h score
84
85 -- | Helper to format one group row result.
86 groupRowFormatHelper :: GroupInfo -> [String]
87 groupRowFormatHelper gi =
88   [ giName gi
89   , printf "%d" $ giNodeCount gi
90   , printf "%d" $ giInstCount gi
91   , printf "%d" $ giBadNodes gi
92   , printf "%d" $ giBadInsts gi
93   , show $ giN1Status gi
94   , printf "%.8f" $ giScore gi
95   ]
96
97 -- | Print node group information.
98 showGroupInfo :: Int -> Group.List -> Node.List -> Instance.List -> IO ()
99 showGroupInfo verbose gl nl il = do
100   let cgrs   = map (\(gdx, (gnl, gil)) ->
101                  calcGroupInfo (Container.find gdx gl) gnl gil) $
102                  Cluster.splitCluster nl il
103       cn1h   = all giN1Status cgrs
104       grs    = map groupRowFormatHelper cgrs
105       header = ["Group", "Nodes", "Instances", "Bad_Nodes", "Bad_Instances",
106                 "N+1", "Score"]
107
108   when (verbose > 1) $
109     printf "Node group information:\n%s"
110            (printTable "  " header grs [False, True, True, True, True,
111                                         False, True])
112
113   printf "Cluster is N+1 %s\n" $ if cn1h then "happy" else "unhappy"
114
115 -- | Gather and print split instances.
116 splitInstancesInfo :: Int -> Node.List -> Instance.List -> IO ()
117 splitInstancesInfo verbose nl il = do
118   let split_insts = Cluster.findSplitInstances nl il
119   if null split_insts
120     then
121       when (verbose > 1) $
122         putStrLn "No split instances found"::IO ()
123     else do
124       putStrLn "Found instances belonging to multiple node groups:"
125       mapM_ (\i -> hPutStrLn stderr $ "  " ++ Instance.name i) split_insts
126
127 -- | Print common (interesting) information.
128 commonInfo :: Int -> Group.List -> Node.List -> Instance.List -> IO ()
129 commonInfo verbose gl nl il = do
130   when (Container.null il && verbose > 1) $
131     printf "Cluster is empty.\n"::IO ()
132
133   let nl_size = Container.size nl
134       il_size = Container.size il
135       gl_size = Container.size gl
136   printf "Loaded %d %s, %d %s, %d %s\n"
137              nl_size (plural nl_size "node" "nodes")
138              il_size (plural il_size "instance" "instances")
139              gl_size (plural gl_size "node group" "node groups")::IO ()
140
141   let csf = commonSuffix nl il
142   when (not (null csf) && verbose > 2) $
143        printf "Note: Stripping common suffix of '%s' from names\n" csf
144
145 -- | Main function.
146 main :: Options -> [String] -> IO ()
147 main opts args = do
148   unless (null args) $ do
149          hPutStrLn stderr "Error: this program doesn't take any arguments."
150          exitWith $ ExitFailure 1
151
152   let verbose = optVerbose opts
153       shownodes = optShowNodes opts
154       showinsts = optShowInsts opts
155
156   (ClusterData gl fixed_nl ilf ctags ipol) <- loadExternalData opts
157
158   putStrLn $ "Loaded cluster tags: " ++ intercalate "," ctags
159
160   when (verbose > 2) .
161        putStrLn $ "Loaded cluster ipolicy: " ++ show ipol
162
163   nlf <- setNodeStatus opts fixed_nl
164
165   commonInfo verbose gl nlf ilf
166
167   splitInstancesInfo verbose nlf ilf
168
169   showGroupInfo verbose gl nlf ilf
170
171   maybePrintInsts showinsts "Instances" (Cluster.printInsts nlf ilf)
172
173   maybePrintNodes shownodes "Cluster" (Cluster.printNodes nlf)
174
175   printf "Cluster coefficients:\n%s" (Cluster.printStats "  " nlf)::IO ()
176   printf "Cluster score: %.8f\n" (Cluster.compCV nlf)