Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / Program / Hcheck.hs @ 4b77c2a2

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