Statistics
| Branch: | Tag: | Revision:

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

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