Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / HTools / Program / Hcheck.hs @ 6d3d13ab

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