Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / Program / Hcheck.hs @ cd67e337

History | View | Annotate | Download (11.7 kB)

1 22e513e7 Agata Murawska
{-| Cluster checker.
2 22e513e7 Agata Murawska
3 22e513e7 Agata Murawska
-}
4 22e513e7 Agata Murawska
5 22e513e7 Agata Murawska
{-
6 22e513e7 Agata Murawska
7 22e513e7 Agata Murawska
Copyright (C) 2012 Google Inc.
8 22e513e7 Agata Murawska
9 22e513e7 Agata Murawska
This program is free software; you can redistribute it and/or modify
10 22e513e7 Agata Murawska
it under the terms of the GNU General Public License as published by
11 22e513e7 Agata Murawska
the Free Software Foundation; either version 2 of the License, or
12 22e513e7 Agata Murawska
(at your option) any later version.
13 22e513e7 Agata Murawska
14 22e513e7 Agata Murawska
This program is distributed in the hope that it will be useful, but
15 22e513e7 Agata Murawska
WITHOUT ANY WARRANTY; without even the implied warranty of
16 22e513e7 Agata Murawska
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 22e513e7 Agata Murawska
General Public License for more details.
18 22e513e7 Agata Murawska
19 22e513e7 Agata Murawska
You should have received a copy of the GNU Gene52al Public License
20 22e513e7 Agata Murawska
along with this program; if not, write to the Free Software
21 22e513e7 Agata Murawska
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 22e513e7 Agata Murawska
02110-1301, USA.
23 22e513e7 Agata Murawska
24 22e513e7 Agata Murawska
-}
25 22e513e7 Agata Murawska
26 22278fa7 Iustin Pop
module Ganeti.HTools.Program.Hcheck
27 22278fa7 Iustin Pop
  ( main
28 22278fa7 Iustin Pop
  , options
29 22278fa7 Iustin Pop
  , arguments
30 22278fa7 Iustin Pop
  ) where
31 22e513e7 Agata Murawska
32 22e513e7 Agata Murawska
import Control.Monad
33 7568b296 Iustin Pop
import Data.List (transpose)
34 22e513e7 Agata Murawska
import System.Exit
35 22e513e7 Agata Murawska
import System.IO
36 1213f9d6 Agata Murawska
import Text.Printf (printf)
37 1213f9d6 Agata Murawska
38 1213f9d6 Agata Murawska
import qualified Ganeti.HTools.Container as Container
39 1213f9d6 Agata Murawska
import qualified Ganeti.HTools.Cluster as Cluster
40 5cdbde9a Agata Murawska
import qualified Ganeti.HTools.Group as Group
41 1213f9d6 Agata Murawska
import qualified Ganeti.HTools.Node as Node
42 1213f9d6 Agata Murawska
import qualified Ganeti.HTools.Instance as Instance
43 1213f9d6 Agata Murawska
44 1213f9d6 Agata Murawska
import qualified Ganeti.HTools.Program.Hbal as Hbal
45 22e513e7 Agata Murawska
46 22278fa7 Iustin Pop
import Ganeti.Common
47 22e513e7 Agata Murawska
import Ganeti.HTools.CLI
48 1213f9d6 Agata Murawska
import Ganeti.HTools.ExtLoader
49 1213f9d6 Agata Murawska
import Ganeti.HTools.Loader
50 1213f9d6 Agata Murawska
import Ganeti.HTools.Types
51 22e513e7 Agata Murawska
52 22e513e7 Agata Murawska
-- | Options list and functions.
53 22e513e7 Agata Murawska
options :: [OptType]
54 22e513e7 Agata Murawska
options =
55 22e513e7 Agata Murawska
  [ oDataFile
56 22e513e7 Agata Murawska
  , oDiskMoves
57 22e513e7 Agata Murawska
  , oDynuFile
58 22e513e7 Agata Murawska
  , oEvacMode
59 22e513e7 Agata Murawska
  , oExInst
60 22e513e7 Agata Murawska
  , oExTags
61 22e513e7 Agata Murawska
  , oIAllocSrc
62 22e513e7 Agata Murawska
  , oInstMoves
63 22e513e7 Agata Murawska
  , oLuxiSocket
64 22e513e7 Agata Murawska
  , oMachineReadable
65 22e513e7 Agata Murawska
  , oMaxCpu
66 22e513e7 Agata Murawska
  , oMaxSolLength
67 22e513e7 Agata Murawska
  , oMinDisk
68 22e513e7 Agata Murawska
  , oMinGain
69 22e513e7 Agata Murawska
  , oMinGainLim
70 22e513e7 Agata Murawska
  , oMinScore
71 22e513e7 Agata Murawska
  , oNoSimulation
72 22e513e7 Agata Murawska
  , oOfflineNode
73 22e513e7 Agata Murawska
  , oQuiet
74 22e513e7 Agata Murawska
  , oRapiMaster
75 22e513e7 Agata Murawska
  , oSelInst
76 22e513e7 Agata Murawska
  , oVerbose
77 22e513e7 Agata Murawska
  ]
78 22e513e7 Agata Murawska
79 22278fa7 Iustin Pop
-- | The list of arguments supported by the program.
80 22278fa7 Iustin Pop
arguments :: [ArgCompletion]
81 22278fa7 Iustin Pop
arguments = []
82 22278fa7 Iustin Pop
83 1213f9d6 Agata Murawska
-- | Check phase - are we before (initial) or after rebalance.
84 1213f9d6 Agata Murawska
data Phase = Initial
85 1213f9d6 Agata Murawska
           | Rebalanced
86 1213f9d6 Agata Murawska
87 22b16dd0 Agata Murawska
-- | Level of presented statistics.
88 22b16dd0 Agata Murawska
data Level = GroupLvl
89 22b16dd0 Agata Murawska
           | ClusterLvl
90 22b16dd0 Agata Murawska
91 7f119c27 Iustin Pop
-- | A type alias for a group index and node\/instance lists.
92 7f119c27 Iustin Pop
type GroupInfo = (Gdx, (Node.List, Instance.List))
93 7f119c27 Iustin Pop
94 7f119c27 Iustin Pop
-- | A type alias for group stats.
95 7f119c27 Iustin Pop
type GroupStats = ((Group.Group, Double), [Int])
96 7f119c27 Iustin Pop
97 d64acbb5 Agata Murawska
-- | Prefix for machine readable names.
98 1213f9d6 Agata Murawska
htcPrefix :: String
99 1213f9d6 Agata Murawska
htcPrefix = "HCHECK"
100 1213f9d6 Agata Murawska
101 1213f9d6 Agata Murawska
-- | Data showed both per group and per cluster.
102 1213f9d6 Agata Murawska
commonData :: [(String, String)]
103 1213f9d6 Agata Murawska
commonData =[ ("N1_FAIL", "Nodes not N+1 happy")
104 1213f9d6 Agata Murawska
            , ("CONFLICT_TAGS", "Nodes with conflicting instances")
105 85890a9d Iustin Pop
            , ("OFFLINE_PRI", "Instances having the primary node offline")
106 85890a9d Iustin Pop
            , ("OFFLINE_SEC", "Instances having a secondary node offline")
107 1213f9d6 Agata Murawska
            ]
108 1213f9d6 Agata Murawska
109 1213f9d6 Agata Murawska
-- | Data showed per group.
110 1213f9d6 Agata Murawska
groupData :: [(String, String)]
111 1213f9d6 Agata Murawska
groupData = commonData ++ [("SCORE", "Group score")]
112 1213f9d6 Agata Murawska
113 1213f9d6 Agata Murawska
-- | Data showed per cluster.
114 1213f9d6 Agata Murawska
clusterData :: [(String, String)]
115 92eacdd8 Agata Murawska
clusterData = commonData ++
116 b1a9d630 Iustin Pop
              [ ("NEED_REBALANCE", "Cluster is not healthy") ]
117 92eacdd8 Agata Murawska
118 b5b19d5e Agata Murawska
-- | Phase-specific prefix for machine readable version.
119 b5b19d5e Agata Murawska
phasePrefix :: Phase -> String
120 b5b19d5e Agata Murawska
phasePrefix Initial = "INIT"
121 b5b19d5e Agata Murawska
phasePrefix Rebalanced = "FINAL"
122 b5b19d5e Agata Murawska
123 b5b19d5e Agata Murawska
-- | Level-specific prefix for machine readable version.
124 b5b19d5e Agata Murawska
levelPrefix :: Level -> String
125 b5b19d5e Agata Murawska
levelPrefix GroupLvl = "GROUP"
126 b5b19d5e Agata Murawska
levelPrefix ClusterLvl = "CLUSTER"
127 b5b19d5e Agata Murawska
128 3c0687b5 Agata Murawska
-- | Machine-readable keys to show depending on given level.
129 3c0687b5 Agata Murawska
keysData :: Level -> [String]
130 3c0687b5 Agata Murawska
keysData GroupLvl = map fst groupData
131 3c0687b5 Agata Murawska
keysData ClusterLvl = map fst clusterData
132 3c0687b5 Agata Murawska
133 b5b19d5e Agata Murawska
-- | Description of phases for human readable version.
134 b5b19d5e Agata Murawska
phaseDescr :: Phase -> String
135 b5b19d5e Agata Murawska
phaseDescr Initial = "initially"
136 b5b19d5e Agata Murawska
phaseDescr Rebalanced = "after rebalancing"
137 b5b19d5e Agata Murawska
138 3c0687b5 Agata Murawska
-- | Description to show depending on given level.
139 3c0687b5 Agata Murawska
descrData :: Level -> [String]
140 3c0687b5 Agata Murawska
descrData GroupLvl = map snd groupData
141 3c0687b5 Agata Murawska
descrData ClusterLvl = map snd clusterData
142 3c0687b5 Agata Murawska
143 3c0687b5 Agata Murawska
-- | Human readable prefix for statistics.
144 3c0687b5 Agata Murawska
phaseLevelDescr :: Phase -> Level -> Maybe String -> String
145 3c0687b5 Agata Murawska
phaseLevelDescr phase GroupLvl (Just name) =
146 3c0687b5 Agata Murawska
    printf "Statistics for group %s %s\n" name $ phaseDescr phase
147 3c0687b5 Agata Murawska
phaseLevelDescr phase GroupLvl Nothing =
148 3c0687b5 Agata Murawska
    printf "Statistics for group %s\n" $ phaseDescr phase
149 3c0687b5 Agata Murawska
phaseLevelDescr phase ClusterLvl _ =
150 3c0687b5 Agata Murawska
    printf "Cluster statistics %s\n" $ phaseDescr phase
151 1213f9d6 Agata Murawska
152 22b16dd0 Agata Murawska
-- | Format a list of key, value as a shell fragment.
153 22b16dd0 Agata Murawska
printKeysHTC :: [(String, String)] -> IO ()
154 22b16dd0 Agata Murawska
printKeysHTC = printKeys htcPrefix
155 22b16dd0 Agata Murawska
156 92eacdd8 Agata Murawska
-- | Prepare string from boolean value.
157 92eacdd8 Agata Murawska
printBool :: Bool    -- ^ Whether the result should be machine readable
158 92eacdd8 Agata Murawska
          -> Bool    -- ^ Value to be converted to string
159 92eacdd8 Agata Murawska
          -> String
160 92eacdd8 Agata Murawska
printBool True True = "1"
161 92eacdd8 Agata Murawska
printBool True False = "0"
162 92eacdd8 Agata Murawska
printBool False b = show b
163 92eacdd8 Agata Murawska
164 81bcbbd3 Iustin Pop
-- | Print mapping from group idx to group uuid (only in machine
165 81bcbbd3 Iustin Pop
-- readable mode).
166 592601b3 Agata Murawska
printGroupsMappings :: Group.List -> IO ()
167 592601b3 Agata Murawska
printGroupsMappings gl = do
168 5b11f8db Iustin Pop
    let extract_vals g = (printf "GROUP_UUID_%d" $ Group.idx g :: String,
169 5b11f8db Iustin Pop
                          Group.uuid g)
170 592601b3 Agata Murawska
        printpairs = map extract_vals (Container.elems gl)
171 592601b3 Agata Murawska
    printKeysHTC printpairs
172 592601b3 Agata Murawska
173 3c0687b5 Agata Murawska
-- | Prepare a single key given a certain level and phase of simulation.
174 3c0687b5 Agata Murawska
prepareKey :: Level -> Phase -> Maybe String -> String -> String
175 3c0687b5 Agata Murawska
prepareKey level phase Nothing suffix =
176 3c0687b5 Agata Murawska
  printf "%s_%s_%s" (phasePrefix phase) (levelPrefix level) suffix
177 3c0687b5 Agata Murawska
prepareKey level phase (Just idx) suffix =
178 3c0687b5 Agata Murawska
  printf "%s_%s_%s_%s" (phasePrefix phase) (levelPrefix level) idx suffix
179 3c0687b5 Agata Murawska
180 3c0687b5 Agata Murawska
-- | Print all the statistics for given level and phase.
181 3c0687b5 Agata Murawska
printStats :: Int            -- ^ Verbosity level
182 3c0687b5 Agata Murawska
           -> Bool           -- ^ If the output should be machine readable
183 3c0687b5 Agata Murawska
           -> Level          -- ^ Level on which we are printing
184 3c0687b5 Agata Murawska
           -> Phase          -- ^ Current phase of simulation
185 3c0687b5 Agata Murawska
           -> [String]       -- ^ Values to print
186 3c0687b5 Agata Murawska
           -> Maybe String   -- ^ Additional data for groups
187 3c0687b5 Agata Murawska
           -> IO ()
188 3c0687b5 Agata Murawska
printStats _ True level phase values gidx = do
189 3c0687b5 Agata Murawska
  let keys = map (prepareKey level phase gidx) (keysData level)
190 3c0687b5 Agata Murawska
  printKeysHTC $ zip keys values
191 3c0687b5 Agata Murawska
192 3c0687b5 Agata Murawska
printStats verbose False level phase values name = do
193 3c0687b5 Agata Murawska
  let prefix = phaseLevelDescr phase level name
194 3c0687b5 Agata Murawska
      descr = descrData level
195 1213f9d6 Agata Murawska
  unless (verbose == 0) $ do
196 4b77c2a2 Iustin Pop
    putStrLn ""
197 4b77c2a2 Iustin Pop
    putStr prefix
198 2cdaf225 Iustin Pop
    mapM_ (uncurry (printf "    %s: %s\n")) (zip descr values)
199 3c0687b5 Agata Murawska
200 3c0687b5 Agata Murawska
-- | Extract name or idx from group.
201 3c0687b5 Agata Murawska
extractGroupData :: Bool -> Group.Group -> String
202 81bcbbd3 Iustin Pop
extractGroupData True grp = show $ Group.idx grp
203 3c0687b5 Agata Murawska
extractGroupData False grp = Group.name grp
204 3c0687b5 Agata Murawska
205 3c0687b5 Agata Murawska
-- | Prepare values for group.
206 3c0687b5 Agata Murawska
prepareGroupValues :: [Int] -> Double -> [String]
207 3c0687b5 Agata Murawska
prepareGroupValues stats score =
208 81bcbbd3 Iustin Pop
  map show stats ++ [printf "%.8f" score]
209 3c0687b5 Agata Murawska
210 3c0687b5 Agata Murawska
-- | Prepare values for cluster.
211 3c0687b5 Agata Murawska
prepareClusterValues :: Bool -> [Int] -> [Bool] -> [String]
212 3c0687b5 Agata Murawska
prepareClusterValues machineread stats bstats =
213 81bcbbd3 Iustin Pop
  map show stats ++ map (printBool machineread) bstats
214 3c0687b5 Agata Murawska
215 3c0687b5 Agata Murawska
-- | Print all the statistics on a group level.
216 7f119c27 Iustin Pop
printGroupStats :: Int -> Bool -> Phase -> GroupStats -> IO ()
217 4b77c2a2 Iustin Pop
printGroupStats verbose machineread phase ((grp, score), stats) = do
218 3c0687b5 Agata Murawska
  let values = prepareGroupValues stats score
219 3c0687b5 Agata Murawska
      extradata = extractGroupData machineread grp
220 3c0687b5 Agata Murawska
  printStats verbose machineread GroupLvl phase values (Just extradata)
221 1213f9d6 Agata Murawska
222 1213f9d6 Agata Murawska
-- | Print all the statistics on a cluster (global) level.
223 b1a9d630 Iustin Pop
printClusterStats :: Int -> Bool -> Phase -> [Int] -> Bool -> IO ()
224 b1a9d630 Iustin Pop
printClusterStats verbose machineread phase stats needhbal = do
225 b1a9d630 Iustin Pop
  let values = prepareClusterValues machineread stats [needhbal]
226 3c0687b5 Agata Murawska
  printStats verbose machineread ClusterLvl phase values Nothing
227 caa97388 Agata Murawska
228 caa97388 Agata Murawska
-- | Check if any of cluster metrics is non-zero.
229 caa97388 Agata Murawska
clusterNeedsRebalance :: [Int] -> Bool
230 caa97388 Agata Murawska
clusterNeedsRebalance stats = sum stats > 0
231 1213f9d6 Agata Murawska
232 1213f9d6 Agata Murawska
{- | Check group for N+1 hapiness, conflicts of primaries on nodes and
233 1213f9d6 Agata Murawska
instances residing on offline nodes.
234 1213f9d6 Agata Murawska
235 1213f9d6 Agata Murawska
-}
236 7f119c27 Iustin Pop
perGroupChecks :: Group.List -> GroupInfo -> GroupStats
237 4b77c2a2 Iustin Pop
perGroupChecks gl (gidx, (nl, il)) =
238 5cdbde9a Agata Murawska
  let grp = Container.find gidx gl
239 5cdbde9a Agata Murawska
      offnl = filter Node.offline (Container.elems nl)
240 2cdaf225 Iustin Pop
      n1violated = length . fst $ Cluster.computeBadItems nl il
241 1213f9d6 Agata Murawska
      conflicttags = length $ filter (>0)
242 1213f9d6 Agata Murawska
                     (map Node.conflictingPrimaries (Container.elems nl))
243 1213f9d6 Agata Murawska
      offline_pri = sum . map length $ map Node.pList offnl
244 1213f9d6 Agata Murawska
      offline_sec = length $ map Node.sList offnl
245 1213f9d6 Agata Murawska
      score = Cluster.compCV nl
246 1213f9d6 Agata Murawska
      groupstats = [ n1violated
247 1213f9d6 Agata Murawska
                   , conflicttags
248 1213f9d6 Agata Murawska
                   , offline_pri
249 1213f9d6 Agata Murawska
                   , offline_sec
250 1213f9d6 Agata Murawska
                   ]
251 4b77c2a2 Iustin Pop
  in ((grp, score), groupstats)
252 1213f9d6 Agata Murawska
253 1213f9d6 Agata Murawska
-- | Use Hbal's iterateDepth to simulate group rebalance.
254 81bcbbd3 Iustin Pop
executeSimulation :: Options -> Cluster.Table -> Double
255 81bcbbd3 Iustin Pop
                  -> Gdx -> Node.List -> Instance.List
256 7f119c27 Iustin Pop
                  -> IO GroupInfo
257 5cc97485 Agata Murawska
executeSimulation opts ini_tbl min_cv gidx nl il = do
258 5cc97485 Agata Murawska
  let imlen = maximum . map (length . Instance.alias) $ Container.elems il
259 5cc97485 Agata Murawska
      nmlen = maximum . map (length . Node.alias) $ Container.elems nl
260 5cc97485 Agata Murawska
261 5cc97485 Agata Murawska
  (fin_tbl, _) <- Hbal.iterateDepth False ini_tbl
262 5cc97485 Agata Murawska
                                    (optMaxLength opts)
263 5cc97485 Agata Murawska
                                    (optDiskMoves opts)
264 5cc97485 Agata Murawska
                                    (optInstMoves opts)
265 5cc97485 Agata Murawska
                                    nmlen imlen [] min_cv
266 5cc97485 Agata Murawska
                                    (optMinGainLim opts) (optMinGain opts)
267 5cc97485 Agata Murawska
                                    (optEvacMode opts)
268 5cc97485 Agata Murawska
269 5cc97485 Agata Murawska
  let (Cluster.Table fin_nl fin_il _ _) = fin_tbl
270 5cc97485 Agata Murawska
  return (gidx, (fin_nl, fin_il))
271 5cc97485 Agata Murawska
272 5cc97485 Agata Murawska
-- | Simulate group rebalance if group's score is not good
273 7f119c27 Iustin Pop
maybeSimulateGroupRebalance :: Options -> GroupInfo -> IO GroupInfo
274 5cc97485 Agata Murawska
maybeSimulateGroupRebalance opts (gidx, (nl, il)) = do
275 1213f9d6 Agata Murawska
  let ini_cv = Cluster.compCV nl
276 1213f9d6 Agata Murawska
      ini_tbl = Cluster.Table nl il ini_cv []
277 1213f9d6 Agata Murawska
      min_cv = optMinScore opts
278 81bcbbd3 Iustin Pop
  if ini_cv < min_cv
279 1213f9d6 Agata Murawska
    then return (gidx, (nl, il))
280 5cc97485 Agata Murawska
    else executeSimulation opts ini_tbl min_cv gidx nl il
281 1213f9d6 Agata Murawska
282 ecc39665 Agata Murawska
-- | Decide whether to simulate rebalance.
283 ecc39665 Agata Murawska
maybeSimulateRebalance :: Bool             -- ^ Whether to simulate rebalance
284 ecc39665 Agata Murawska
                       -> Options          -- ^ Command line options
285 7f119c27 Iustin Pop
                       -> [GroupInfo]      -- ^ Group data
286 7f119c27 Iustin Pop
                       -> IO [GroupInfo]
287 ecc39665 Agata Murawska
maybeSimulateRebalance True opts cluster =
288 5cc97485 Agata Murawska
    mapM (maybeSimulateGroupRebalance opts) cluster
289 ecc39665 Agata Murawska
maybeSimulateRebalance False _ cluster = return cluster
290 ecc39665 Agata Murawska
291 1213f9d6 Agata Murawska
-- | Prints the final @OK@ marker in machine readable output.
292 1213f9d6 Agata Murawska
printFinalHTC :: Bool -> IO ()
293 1213f9d6 Agata Murawska
printFinalHTC = printFinal htcPrefix
294 1213f9d6 Agata Murawska
295 22e513e7 Agata Murawska
-- | Main function.
296 22e513e7 Agata Murawska
main :: Options -> [String] -> IO ()
297 1213f9d6 Agata Murawska
main opts args = do
298 22e513e7 Agata Murawska
  unless (null args) $ do
299 22e513e7 Agata Murawska
         hPutStrLn stderr "Error: this program doesn't take any arguments."
300 22e513e7 Agata Murawska
         exitWith $ ExitFailure 1
301 1213f9d6 Agata Murawska
302 1213f9d6 Agata Murawska
  let verbose = optVerbose opts
303 1213f9d6 Agata Murawska
      machineread = optMachineReadable opts
304 1213f9d6 Agata Murawska
      nosimulation = optNoSimulation opts
305 1213f9d6 Agata Murawska
306 5cdbde9a Agata Murawska
  (ClusterData gl fixed_nl ilf _ _) <- loadExternalData opts
307 1213f9d6 Agata Murawska
  nlf <- setNodeStatus opts fixed_nl
308 1213f9d6 Agata Murawska
309 b1a9d630 Iustin Pop
  let splitcluster = Cluster.splitCluster nlf ilf
310 1213f9d6 Agata Murawska
311 592601b3 Agata Murawska
  when machineread $ printGroupsMappings gl
312 5cdbde9a Agata Murawska
313 4b77c2a2 Iustin Pop
  let groupsstats = map (perGroupChecks gl) splitcluster
314 4b77c2a2 Iustin Pop
      clusterstats = map sum . transpose . map snd $ groupsstats
315 caa97388 Agata Murawska
      needrebalance = clusterNeedsRebalance clusterstats
316 1213f9d6 Agata Murawska
317 4b77c2a2 Iustin Pop
  unless (verbose == 0 || machineread) $
318 4b77c2a2 Iustin Pop
    if nosimulation
319 4b77c2a2 Iustin Pop
      then putStrLn "Running in no-simulation mode."
320 4b77c2a2 Iustin Pop
      else if needrebalance
321 4b77c2a2 Iustin Pop
             then putStrLn "Cluster needs rebalancing."
322 4b77c2a2 Iustin Pop
             else putStrLn "No need to rebalance cluster, no problems found."
323 4b77c2a2 Iustin Pop
324 4b77c2a2 Iustin Pop
  mapM_ (printGroupStats verbose machineread Initial) groupsstats
325 1213f9d6 Agata Murawska
326 4b77c2a2 Iustin Pop
  printClusterStats verbose machineread Initial clusterstats needrebalance
327 1213f9d6 Agata Murawska
328 ecc39665 Agata Murawska
  let exitOK = nosimulation || not needrebalance
329 b1a9d630 Iustin Pop
      simulate = not nosimulation && needrebalance
330 ecc39665 Agata Murawska
331 ecc39665 Agata Murawska
  rebalancedcluster <- maybeSimulateRebalance simulate opts splitcluster
332 ecc39665 Agata Murawska
333 ecc39665 Agata Murawska
  when (simulate || machineread) $ do
334 4b77c2a2 Iustin Pop
    let newgroupstats = map (perGroupChecks gl) rebalancedcluster
335 4b77c2a2 Iustin Pop
        newclusterstats = map sum . transpose . map snd $ newgroupstats
336 caa97388 Agata Murawska
        newneedrebalance = clusterNeedsRebalance clusterstats
337 ecc39665 Agata Murawska
338 4b77c2a2 Iustin Pop
    mapM_ (printGroupStats verbose machineread Rebalanced) newgroupstats
339 4b77c2a2 Iustin Pop
340 caa97388 Agata Murawska
    printClusterStats verbose machineread Rebalanced newclusterstats
341 b1a9d630 Iustin Pop
                           newneedrebalance
342 1213f9d6 Agata Murawska
343 1213f9d6 Agata Murawska
  printFinalHTC machineread
344 ecc39665 Agata Murawska
345 2cdaf225 Iustin Pop
  unless exitOK . exitWith $ ExitFailure 1