Revision f23f21c3

b/htools/Ganeti/HTools/Cluster.hs
27 27
-}
28 28

  
29 29
module Ganeti.HTools.Cluster
30
    (
31
     -- * Types
32
      AllocSolution(..)
33
    , EvacSolution(..)
34
    , Table(..)
35
    , CStats(..)
36
    , AllocStats
37
    , AllocResult
38
    , AllocMethod
39
    -- * Generic functions
40
    , totalResources
41
    , computeAllocationDelta
42
    -- * First phase functions
43
    , computeBadItems
44
    -- * Second phase functions
45
    , printSolutionLine
46
    , formatCmds
47
    , involvedNodes
48
    , splitJobs
49
    -- * Display functions
50
    , printNodes
51
    , printInsts
52
    -- * Balacing functions
53
    , checkMove
54
    , doNextBalance
55
    , tryBalance
56
    , compCV
57
    , compCVNodes
58
    , compDetailedCV
59
    , printStats
60
    , iMoveToJob
61
    -- * IAllocator functions
62
    , genAllocNodes
63
    , tryAlloc
64
    , tryMGAlloc
65
    , tryReloc
66
    , tryNodeEvac
67
    , tryChangeGroup
68
    , collapseFailures
69
    -- * Allocation functions
70
    , iterateAlloc
71
    , tieredAlloc
72
     -- * Node group functions
73
    , instanceGroup
74
    , findSplitInstances
75
    , splitCluster
76
    ) where
30
  (
31
    -- * Types
32
    AllocSolution(..)
33
  , EvacSolution(..)
34
  , Table(..)
35
  , CStats(..)
36
  , AllocStats
37
  , AllocResult
38
  , AllocMethod
39
  -- * Generic functions
40
  , totalResources
41
  , computeAllocationDelta
42
  -- * First phase functions
43
  , computeBadItems
44
  -- * Second phase functions
45
  , printSolutionLine
46
  , formatCmds
47
  , involvedNodes
48
  , splitJobs
49
  -- * Display functions
50
  , printNodes
51
  , printInsts
52
  -- * Balacing functions
53
  , checkMove
54
  , doNextBalance
55
  , tryBalance
56
  , compCV
57
  , compCVNodes
58
  , compDetailedCV
59
  , printStats
60
  , iMoveToJob
61
  -- * IAllocator functions
62
  , genAllocNodes
63
  , tryAlloc
64
  , tryMGAlloc
65
  , tryReloc
66
  , tryNodeEvac
67
  , tryChangeGroup
68
  , collapseFailures
69
  -- * Allocation functions
70
  , iterateAlloc
71
  , tieredAlloc
72
  -- * Node group functions
73
  , instanceGroup
74
  , findSplitInstances
75
  , splitCluster
76
  ) where
77 77

  
78 78
import qualified Data.IntSet as IntSet
79 79
import Data.List
......
104 104
-- type consists of actual opcodes (a restricted subset) that are
105 105
-- transmitted back to Ganeti.
106 106
data EvacSolution = EvacSolution
107
    { esMoved   :: [(Idx, Gdx, [Ndx])]  -- ^ Instances moved successfully
108
    , esFailed  :: [(Idx, String)]      -- ^ Instances which were not
109
                                        -- relocated
110
    , esOpCodes :: [[OpCodes.OpCode]]   -- ^ List of jobs
111
    }
107
  { esMoved   :: [(Idx, Gdx, [Ndx])]  -- ^ Instances moved successfully
108
  , esFailed  :: [(Idx, String)]      -- ^ Instances which were not
109
                                      -- relocated
110
  , esOpCodes :: [[OpCodes.OpCode]]   -- ^ List of jobs
111
  }
112 112

  
113 113
-- | Allocation results, as used in 'iterateAlloc' and 'tieredAlloc'.
114 114
type AllocResult = (FailStats, Node.List, Instance.List,
......
207 207
-- | Update stats with data from a new node.
208 208
updateCStats :: CStats -> Node.Node -> CStats
209 209
updateCStats cs node =
210
    let CStats { csFmem = x_fmem, csFdsk = x_fdsk,
211
                 csAmem = x_amem, csAcpu = x_acpu, csAdsk = x_adsk,
212
                 csMmem = x_mmem, csMdsk = x_mdsk, csMcpu = x_mcpu,
213
                 csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu,
214
                 csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu,
215
                 csVcpu = x_vcpu,
216
                 csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst
217
               }
218
            = cs
219
        inc_amem = Node.fMem node - Node.rMem node
220
        inc_amem' = if inc_amem > 0 then inc_amem else 0
221
        inc_adsk = Node.availDisk node
222
        inc_imem = truncate (Node.tMem node) - Node.nMem node
223
                   - Node.xMem node - Node.fMem node
224
        inc_icpu = Node.uCpu node
225
        inc_idsk = truncate (Node.tDsk node) - Node.fDsk node
226
        inc_vcpu = Node.hiCpu node
227
        inc_acpu = Node.availCpu node
228

  
229
    in cs { csFmem = x_fmem + fromIntegral (Node.fMem node)
230
          , csFdsk = x_fdsk + fromIntegral (Node.fDsk node)
231
          , csAmem = x_amem + fromIntegral inc_amem'
232
          , csAdsk = x_adsk + fromIntegral inc_adsk
233
          , csAcpu = x_acpu + fromIntegral inc_acpu
234
          , csMmem = max x_mmem (fromIntegral inc_amem')
235
          , csMdsk = max x_mdsk (fromIntegral inc_adsk)
236
          , csMcpu = max x_mcpu (fromIntegral inc_acpu)
237
          , csImem = x_imem + fromIntegral inc_imem
238
          , csIdsk = x_idsk + fromIntegral inc_idsk
239
          , csIcpu = x_icpu + fromIntegral inc_icpu
240
          , csTmem = x_tmem + Node.tMem node
241
          , csTdsk = x_tdsk + Node.tDsk node
242
          , csTcpu = x_tcpu + Node.tCpu node
243
          , csVcpu = x_vcpu + fromIntegral inc_vcpu
244
          , csXmem = x_xmem + fromIntegral (Node.xMem node)
245
          , csNmem = x_nmem + fromIntegral (Node.nMem node)
246
          , csNinst = x_ninst + length (Node.pList node)
247
          }
210
  let CStats { csFmem = x_fmem, csFdsk = x_fdsk,
211
               csAmem = x_amem, csAcpu = x_acpu, csAdsk = x_adsk,
212
               csMmem = x_mmem, csMdsk = x_mdsk, csMcpu = x_mcpu,
213
               csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu,
214
               csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu,
215
               csVcpu = x_vcpu,
216
               csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst
217
             }
218
        = cs
219
      inc_amem = Node.fMem node - Node.rMem node
220
      inc_amem' = if inc_amem > 0 then inc_amem else 0
221
      inc_adsk = Node.availDisk node
222
      inc_imem = truncate (Node.tMem node) - Node.nMem node
223
                 - Node.xMem node - Node.fMem node
224
      inc_icpu = Node.uCpu node
225
      inc_idsk = truncate (Node.tDsk node) - Node.fDsk node
226
      inc_vcpu = Node.hiCpu node
227
      inc_acpu = Node.availCpu node
228
  in cs { csFmem = x_fmem + fromIntegral (Node.fMem node)
229
        , csFdsk = x_fdsk + fromIntegral (Node.fDsk node)
230
        , csAmem = x_amem + fromIntegral inc_amem'
231
        , csAdsk = x_adsk + fromIntegral inc_adsk
232
        , csAcpu = x_acpu + fromIntegral inc_acpu
233
        , csMmem = max x_mmem (fromIntegral inc_amem')
234
        , csMdsk = max x_mdsk (fromIntegral inc_adsk)
235
        , csMcpu = max x_mcpu (fromIntegral inc_acpu)
236
        , csImem = x_imem + fromIntegral inc_imem
237
        , csIdsk = x_idsk + fromIntegral inc_idsk
238
        , csIcpu = x_icpu + fromIntegral inc_icpu
239
        , csTmem = x_tmem + Node.tMem node
240
        , csTdsk = x_tdsk + Node.tDsk node
241
        , csTcpu = x_tcpu + Node.tCpu node
242
        , csVcpu = x_vcpu + fromIntegral inc_vcpu
243
        , csXmem = x_xmem + fromIntegral (Node.xMem node)
244
        , csNmem = x_nmem + fromIntegral (Node.nMem node)
245
        , csNinst = x_ninst + length (Node.pList node)
246
        }
248 247

  
249 248
-- | Compute the total free disk and memory in the cluster.
250 249
totalResources :: Node.List -> CStats
251 250
totalResources nl =
252
    let cs = foldl' updateCStats emptyCStats . Container.elems $ nl
253
    in cs { csScore = compCV nl }
251
  let cs = foldl' updateCStats emptyCStats . Container.elems $ nl
252
  in cs { csScore = compCV nl }
254 253

  
255 254
-- | Compute the delta between two cluster state.
256 255
--
......
260 259
-- was left unallocated.
261 260
computeAllocationDelta :: CStats -> CStats -> AllocStats
262 261
computeAllocationDelta cini cfin =
263
    let CStats {csImem = i_imem, csIdsk = i_idsk, csIcpu = i_icpu} = cini
264
        CStats {csImem = f_imem, csIdsk = f_idsk, csIcpu = f_icpu,
265
                csTmem = t_mem, csTdsk = t_dsk, csVcpu = v_cpu } = cfin
266
        rini = RSpec (fromIntegral i_icpu) (fromIntegral i_imem)
267
               (fromIntegral i_idsk)
268
        rfin = RSpec (fromIntegral (f_icpu - i_icpu))
269
               (fromIntegral (f_imem - i_imem))
270
               (fromIntegral (f_idsk - i_idsk))
271
        un_cpu = fromIntegral (v_cpu - f_icpu)::Int
272
        runa = RSpec un_cpu (truncate t_mem - fromIntegral f_imem)
273
               (truncate t_dsk - fromIntegral f_idsk)
274
    in (rini, rfin, runa)
262
  let CStats {csImem = i_imem, csIdsk = i_idsk, csIcpu = i_icpu} = cini
263
      CStats {csImem = f_imem, csIdsk = f_idsk, csIcpu = f_icpu,
264
              csTmem = t_mem, csTdsk = t_dsk, csVcpu = v_cpu } = cfin
265
      rini = RSpec (fromIntegral i_icpu) (fromIntegral i_imem)
266
             (fromIntegral i_idsk)
267
      rfin = RSpec (fromIntegral (f_icpu - i_icpu))
268
             (fromIntegral (f_imem - i_imem))
269
             (fromIntegral (f_idsk - i_idsk))
270
      un_cpu = fromIntegral (v_cpu - f_icpu)::Int
271
      runa = RSpec un_cpu (truncate t_mem - fromIntegral f_imem)
272
             (truncate t_dsk - fromIntegral f_idsk)
273
  in (rini, rfin, runa)
275 274

  
276 275
-- | The names and weights of the individual elements in the CV list.
277 276
detailedCVInfo :: [(Double, String)]
......
296 295
-- | Compute the mem and disk covariance.
297 296
compDetailedCV :: [Node.Node] -> [Double]
298 297
compDetailedCV all_nodes =
299
    let
300
        (offline, nodes) = partition Node.offline all_nodes
301
        mem_l = map Node.pMem nodes
302
        dsk_l = map Node.pDsk nodes
303
        -- metric: memory covariance
304
        mem_cv = stdDev mem_l
305
        -- metric: disk covariance
306
        dsk_cv = stdDev dsk_l
307
        -- metric: count of instances living on N1 failing nodes
308
        n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) +
309
                                                   length (Node.pList n)) .
310
                   filter Node.failN1 $ nodes :: Double
311
        res_l = map Node.pRem nodes
312
        -- metric: reserved memory covariance
313
        res_cv = stdDev res_l
314
        -- offline instances metrics
315
        offline_ipri = sum . map (length . Node.pList) $ offline
316
        offline_isec = sum . map (length . Node.sList) $ offline
317
        -- metric: count of instances on offline nodes
318
        off_score = fromIntegral (offline_ipri + offline_isec)::Double
319
        -- metric: count of primary instances on offline nodes (this
320
        -- helps with evacuation/failover of primary instances on
321
        -- 2-node clusters with one node offline)
322
        off_pri_score = fromIntegral offline_ipri::Double
323
        cpu_l = map Node.pCpu nodes
324
        -- metric: covariance of vcpu/pcpu ratio
325
        cpu_cv = stdDev cpu_l
326
        -- metrics: covariance of cpu, memory, disk and network load
327
        (c_load, m_load, d_load, n_load) = unzip4 $
328
            map (\n ->
329
                     let DynUtil c1 m1 d1 n1 = Node.utilLoad n
330
                         DynUtil c2 m2 d2 n2 = Node.utilPool n
331
                     in (c1/c2, m1/m2, d1/d2, n1/n2)
332
                ) nodes
333
        -- metric: conflicting instance count
334
        pri_tags_inst = sum $ map Node.conflictingPrimaries nodes
335
        pri_tags_score = fromIntegral pri_tags_inst::Double
336
    in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv
337
       , stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load
338
       , pri_tags_score ]
298
  let (offline, nodes) = partition Node.offline all_nodes
299
      mem_l = map Node.pMem nodes
300
      dsk_l = map Node.pDsk nodes
301
      -- metric: memory covariance
302
      mem_cv = stdDev mem_l
303
      -- metric: disk covariance
304
      dsk_cv = stdDev dsk_l
305
      -- metric: count of instances living on N1 failing nodes
306
      n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) +
307
                                                 length (Node.pList n)) .
308
                 filter Node.failN1 $ nodes :: Double
309
      res_l = map Node.pRem nodes
310
      -- metric: reserved memory covariance
311
      res_cv = stdDev res_l
312
      -- offline instances metrics
313
      offline_ipri = sum . map (length . Node.pList) $ offline
314
      offline_isec = sum . map (length . Node.sList) $ offline
315
      -- metric: count of instances on offline nodes
316
      off_score = fromIntegral (offline_ipri + offline_isec)::Double
317
      -- metric: count of primary instances on offline nodes (this
318
      -- helps with evacuation/failover of primary instances on
319
      -- 2-node clusters with one node offline)
320
      off_pri_score = fromIntegral offline_ipri::Double
321
      cpu_l = map Node.pCpu nodes
322
      -- metric: covariance of vcpu/pcpu ratio
323
      cpu_cv = stdDev cpu_l
324
      -- metrics: covariance of cpu, memory, disk and network load
325
      (c_load, m_load, d_load, n_load) =
326
        unzip4 $ map (\n ->
327
                      let DynUtil c1 m1 d1 n1 = Node.utilLoad n
328
                          DynUtil c2 m2 d2 n2 = Node.utilPool n
329
                      in (c1/c2, m1/m2, d1/d2, n1/n2)) nodes
330
      -- metric: conflicting instance count
331
      pri_tags_inst = sum $ map Node.conflictingPrimaries nodes
332
      pri_tags_score = fromIntegral pri_tags_inst::Double
333
  in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv
334
     , stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load
335
     , pri_tags_score ]
339 336

  
340 337
-- | Compute the /total/ variance.
341 338
compCVNodes :: [Node.Node] -> Double
......
354 351
-- | Compute best table. Note that the ordering of the arguments is important.
355 352
compareTables :: Table -> Table -> Table
356 353
compareTables a@(Table _ _ a_cv _) b@(Table _ _ b_cv _ ) =
357
    if a_cv > b_cv then b else a
354
  if a_cv > b_cv then b else a
358 355

  
359 356
-- | Applies an instance move to a given node list and instance.
360 357
applyMove :: Node.List -> Instance.Instance
361 358
          -> IMove -> OpResult (Node.List, Instance.Instance, Ndx, Ndx)
362 359
-- Failover (f)
363 360
applyMove nl inst Failover =
364
    let old_pdx = Instance.pNode inst
365
        old_sdx = Instance.sNode inst
366
        old_p = Container.find old_pdx nl
367
        old_s = Container.find old_sdx nl
368
        int_p = Node.removePri old_p inst
369
        int_s = Node.removeSec old_s inst
370
        force_p = Node.offline old_p
371
        new_nl = do -- Maybe monad
372
          new_p <- Node.addPriEx force_p int_s inst
373
          new_s <- Node.addSec int_p inst old_sdx
374
          let new_inst = Instance.setBoth inst old_sdx old_pdx
375
          return (Container.addTwo old_pdx new_s old_sdx new_p nl,
376
                  new_inst, old_sdx, old_pdx)
377
    in new_nl
361
  let old_pdx = Instance.pNode inst
362
      old_sdx = Instance.sNode inst
363
      old_p = Container.find old_pdx nl
364
      old_s = Container.find old_sdx nl
365
      int_p = Node.removePri old_p inst
366
      int_s = Node.removeSec old_s inst
367
      force_p = Node.offline old_p
368
      new_nl = do -- Maybe monad
369
        new_p <- Node.addPriEx force_p int_s inst
370
        new_s <- Node.addSec int_p inst old_sdx
371
        let new_inst = Instance.setBoth inst old_sdx old_pdx
372
        return (Container.addTwo old_pdx new_s old_sdx new_p nl,
373
                new_inst, old_sdx, old_pdx)
374
  in new_nl
378 375

  
379 376
-- Replace the primary (f:, r:np, f)
380 377
applyMove nl inst (ReplacePrimary new_pdx) =
381
    let old_pdx = Instance.pNode inst
382
        old_sdx = Instance.sNode inst
383
        old_p = Container.find old_pdx nl
384
        old_s = Container.find old_sdx nl
385
        tgt_n = Container.find new_pdx nl
386
        int_p = Node.removePri old_p inst
387
        int_s = Node.removeSec old_s inst
388
        force_p = Node.offline old_p
389
        new_nl = do -- Maybe monad
390
          -- check that the current secondary can host the instance
391
          -- during the migration
392
          tmp_s <- Node.addPriEx force_p int_s inst
393
          let tmp_s' = Node.removePri tmp_s inst
394
          new_p <- Node.addPriEx force_p tgt_n inst
395
          new_s <- Node.addSecEx force_p tmp_s' inst new_pdx
396
          let new_inst = Instance.setPri inst new_pdx
397
          return (Container.add new_pdx new_p $
398
                  Container.addTwo old_pdx int_p old_sdx new_s nl,
399
                  new_inst, new_pdx, old_sdx)
400
    in new_nl
378
  let old_pdx = Instance.pNode inst
379
      old_sdx = Instance.sNode inst
380
      old_p = Container.find old_pdx nl
381
      old_s = Container.find old_sdx nl
382
      tgt_n = Container.find new_pdx nl
383
      int_p = Node.removePri old_p inst
384
      int_s = Node.removeSec old_s inst
385
      force_p = Node.offline old_p
386
      new_nl = do -- Maybe monad
387
                  -- check that the current secondary can host the instance
388
                  -- during the migration
389
        tmp_s <- Node.addPriEx force_p int_s inst
390
        let tmp_s' = Node.removePri tmp_s inst
391
        new_p <- Node.addPriEx force_p tgt_n inst
392
        new_s <- Node.addSecEx force_p tmp_s' inst new_pdx
393
        let new_inst = Instance.setPri inst new_pdx
394
        return (Container.add new_pdx new_p $
395
                Container.addTwo old_pdx int_p old_sdx new_s nl,
396
                new_inst, new_pdx, old_sdx)
397
  in new_nl
401 398

  
402 399
-- Replace the secondary (r:ns)
403 400
applyMove nl inst (ReplaceSecondary new_sdx) =
404
    let old_pdx = Instance.pNode inst
405
        old_sdx = Instance.sNode inst
406
        old_s = Container.find old_sdx nl
407
        tgt_n = Container.find new_sdx nl
408
        int_s = Node.removeSec old_s inst
409
        force_s = Node.offline old_s
410
        new_inst = Instance.setSec inst new_sdx
411
        new_nl = Node.addSecEx force_s tgt_n inst old_pdx >>=
412
                 \new_s -> return (Container.addTwo new_sdx
413
                                   new_s old_sdx int_s nl,
414
                                   new_inst, old_pdx, new_sdx)
415
    in new_nl
401
  let old_pdx = Instance.pNode inst
402
      old_sdx = Instance.sNode inst
403
      old_s = Container.find old_sdx nl
404
      tgt_n = Container.find new_sdx nl
405
      int_s = Node.removeSec old_s inst
406
      force_s = Node.offline old_s
407
      new_inst = Instance.setSec inst new_sdx
408
      new_nl = Node.addSecEx force_s tgt_n inst old_pdx >>=
409
               \new_s -> return (Container.addTwo new_sdx
410
                                 new_s old_sdx int_s nl,
411
                                 new_inst, old_pdx, new_sdx)
412
  in new_nl
416 413

  
417 414
-- Replace the secondary and failover (r:np, f)
418 415
applyMove nl inst (ReplaceAndFailover new_pdx) =
419
    let old_pdx = Instance.pNode inst
420
        old_sdx = Instance.sNode inst
421
        old_p = Container.find old_pdx nl
422
        old_s = Container.find old_sdx nl
423
        tgt_n = Container.find new_pdx nl
424
        int_p = Node.removePri old_p inst
425
        int_s = Node.removeSec old_s inst
426
        force_s = Node.offline old_s
427
        new_nl = do -- Maybe monad
428
          new_p <- Node.addPri tgt_n inst
429
          new_s <- Node.addSecEx force_s int_p inst new_pdx
430
          let new_inst = Instance.setBoth inst new_pdx old_pdx
431
          return (Container.add new_pdx new_p $
432
                  Container.addTwo old_pdx new_s old_sdx int_s nl,
433
                  new_inst, new_pdx, old_pdx)
434
    in new_nl
416
  let old_pdx = Instance.pNode inst
417
      old_sdx = Instance.sNode inst
418
      old_p = Container.find old_pdx nl
419
      old_s = Container.find old_sdx nl
420
      tgt_n = Container.find new_pdx nl
421
      int_p = Node.removePri old_p inst
422
      int_s = Node.removeSec old_s inst
423
      force_s = Node.offline old_s
424
      new_nl = do -- Maybe monad
425
        new_p <- Node.addPri tgt_n inst
426
        new_s <- Node.addSecEx force_s int_p inst new_pdx
427
        let new_inst = Instance.setBoth inst new_pdx old_pdx
428
        return (Container.add new_pdx new_p $
429
                Container.addTwo old_pdx new_s old_sdx int_s nl,
430
                new_inst, new_pdx, old_pdx)
431
  in new_nl
435 432

  
436 433
-- Failver and replace the secondary (f, r:ns)
437 434
applyMove nl inst (FailoverAndReplace new_sdx) =
438
    let old_pdx = Instance.pNode inst
439
        old_sdx = Instance.sNode inst
440
        old_p = Container.find old_pdx nl
441
        old_s = Container.find old_sdx nl
442
        tgt_n = Container.find new_sdx nl
443
        int_p = Node.removePri old_p inst
444
        int_s = Node.removeSec old_s inst
445
        force_p = Node.offline old_p
446
        new_nl = do -- Maybe monad
447
          new_p <- Node.addPriEx force_p int_s inst
448
          new_s <- Node.addSecEx force_p tgt_n inst old_sdx
449
          let new_inst = Instance.setBoth inst old_sdx new_sdx
450
          return (Container.add new_sdx new_s $
451
                  Container.addTwo old_sdx new_p old_pdx int_p nl,
452
                  new_inst, old_sdx, new_sdx)
453
    in new_nl
435
  let old_pdx = Instance.pNode inst
436
      old_sdx = Instance.sNode inst
437
      old_p = Container.find old_pdx nl
438
      old_s = Container.find old_sdx nl
439
      tgt_n = Container.find new_sdx nl
440
      int_p = Node.removePri old_p inst
441
      int_s = Node.removeSec old_s inst
442
      force_p = Node.offline old_p
443
      new_nl = do -- Maybe monad
444
        new_p <- Node.addPriEx force_p int_s inst
445
        new_s <- Node.addSecEx force_p tgt_n inst old_sdx
446
        let new_inst = Instance.setBoth inst old_sdx new_sdx
447
        return (Container.add new_sdx new_s $
448
                Container.addTwo old_sdx new_p old_pdx int_p nl,
449
                new_inst, old_sdx, new_sdx)
450
  in new_nl
454 451

  
455 452
-- | Tries to allocate an instance on one given node.
456 453
allocateOnSingle :: Node.List -> Instance.Instance -> Ndx
457 454
                 -> OpResult Node.AllocElement
458 455
allocateOnSingle nl inst new_pdx =
459
    let p = Container.find new_pdx nl
460
        new_inst = Instance.setBoth inst new_pdx Node.noSecondary
461
    in  Node.addPri p inst >>= \new_p -> do
462
      let new_nl = Container.add new_pdx new_p nl
463
          new_score = compCV nl
464
      return (new_nl, new_inst, [new_p], new_score)
456
  let p = Container.find new_pdx nl
457
      new_inst = Instance.setBoth inst new_pdx Node.noSecondary
458
  in  Node.addPri p inst >>= \new_p -> do
459
    let new_nl = Container.add new_pdx new_p nl
460
        new_score = compCV nl
461
    return (new_nl, new_inst, [new_p], new_score)
465 462

  
466 463
-- | Tries to allocate an instance on a given pair of nodes.
467 464
allocateOnPair :: Node.List -> Instance.Instance -> Ndx -> Ndx
468 465
               -> OpResult Node.AllocElement
469 466
allocateOnPair nl inst new_pdx new_sdx =
470
    let tgt_p = Container.find new_pdx nl
471
        tgt_s = Container.find new_sdx nl
472
    in do
473
      new_p <- Node.addPri tgt_p inst
474
      new_s <- Node.addSec tgt_s inst new_pdx
475
      let new_inst = Instance.setBoth inst new_pdx new_sdx
476
          new_nl = Container.addTwo new_pdx new_p new_sdx new_s nl
477
      return (new_nl, new_inst, [new_p, new_s], compCV new_nl)
467
  let tgt_p = Container.find new_pdx nl
468
      tgt_s = Container.find new_sdx nl
469
  in do
470
    new_p <- Node.addPri tgt_p inst
471
    new_s <- Node.addSec tgt_s inst new_pdx
472
    let new_inst = Instance.setBoth inst new_pdx new_sdx
473
        new_nl = Container.addTwo new_pdx new_p new_sdx new_s nl
474
    return (new_nl, new_inst, [new_p, new_s], compCV new_nl)
478 475

  
479 476
-- | Tries to perform an instance move and returns the best table
480 477
-- between the original one and the new one.
......
484 481
                -> IMove -- ^ The move to apply
485 482
                -> Table -- ^ The final best table
486 483
checkSingleStep ini_tbl target cur_tbl move =
487
    let
488
        Table ini_nl ini_il _ ini_plc = ini_tbl
489
        tmp_resu = applyMove ini_nl target move
490
    in
491
      case tmp_resu of
492
        OpFail _ -> cur_tbl
493
        OpGood (upd_nl, new_inst, pri_idx, sec_idx) ->
494
            let tgt_idx = Instance.idx target
495
                upd_cvar = compCV upd_nl
496
                upd_il = Container.add tgt_idx new_inst ini_il
497
                upd_plc = (tgt_idx, pri_idx, sec_idx, move, upd_cvar):ini_plc
498
                upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
499
            in
500
              compareTables cur_tbl upd_tbl
484
  let Table ini_nl ini_il _ ini_plc = ini_tbl
485
      tmp_resu = applyMove ini_nl target move
486
  in case tmp_resu of
487
       OpFail _ -> cur_tbl
488
       OpGood (upd_nl, new_inst, pri_idx, sec_idx) ->
489
         let tgt_idx = Instance.idx target
490
             upd_cvar = compCV upd_nl
491
             upd_il = Container.add tgt_idx new_inst ini_il
492
             upd_plc = (tgt_idx, pri_idx, sec_idx, move, upd_cvar):ini_plc
493
             upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
494
         in compareTables cur_tbl upd_tbl
501 495

  
502 496
-- | Given the status of the current secondary as a valid new node and
503 497
-- the current candidate target node, generate the possible moves for
......
508 502
              -> [IMove]   -- ^ List of valid result moves
509 503

  
510 504
possibleMoves _ False tdx =
511
    [ReplaceSecondary tdx]
505
  [ReplaceSecondary tdx]
512 506

  
513 507
possibleMoves True True tdx =
514
    [ReplaceSecondary tdx,
515
     ReplaceAndFailover tdx,
516
     ReplacePrimary tdx,
517
     FailoverAndReplace tdx]
508
  [ ReplaceSecondary tdx
509
  , ReplaceAndFailover tdx
510
  , ReplacePrimary tdx
511
  , FailoverAndReplace tdx
512
  ]
518 513

  
519 514
possibleMoves False True tdx =
520
    [ReplaceSecondary tdx,
521
     ReplaceAndFailover tdx]
515
  [ ReplaceSecondary tdx
516
  , ReplaceAndFailover tdx
517
  ]
522 518

  
523 519
-- | Compute the best move for a given instance.
524 520
checkInstanceMove :: [Ndx]             -- ^ Allowed target node indices
......
528 524
                  -> Instance.Instance -- ^ Instance to move
529 525
                  -> Table             -- ^ Best new table for this instance
530 526
checkInstanceMove nodes_idx disk_moves inst_moves ini_tbl target =
531
    let
532
        opdx = Instance.pNode target
533
        osdx = Instance.sNode target
534
        nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx
535
        use_secondary = elem osdx nodes_idx && inst_moves
536
        aft_failover = if use_secondary -- if allowed to failover
527
  let opdx = Instance.pNode target
528
      osdx = Instance.sNode target
529
      nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx
530
      use_secondary = elem osdx nodes_idx && inst_moves
531
      aft_failover = if use_secondary -- if allowed to failover
537 532
                       then checkSingleStep ini_tbl target ini_tbl Failover
538 533
                       else ini_tbl
539
        all_moves = if disk_moves
534
      all_moves = if disk_moves
540 535
                    then concatMap
541
                         (possibleMoves use_secondary inst_moves) nodes
536
                           (possibleMoves use_secondary inst_moves) nodes
542 537
                    else []
543 538
    in
544 539
      -- iterate over the possible nodes for this instance
......
552 547
          -> [Instance.Instance] -- ^ List of instances still to move
553 548
          -> Table               -- ^ The new solution
554 549
checkMove nodes_idx disk_moves inst_moves ini_tbl victims =
555
    let Table _ _ _ ini_plc = ini_tbl
556
        -- we're using rwhnf from the Control.Parallel.Strategies
557
        -- package; we don't need to use rnf as that would force too
558
        -- much evaluation in single-threaded cases, and in
559
        -- multi-threaded case the weak head normal form is enough to
560
        -- spark the evaluation
561
        tables = parMap rwhnf (checkInstanceMove nodes_idx disk_moves
562
                               inst_moves ini_tbl)
563
                 victims
564
        -- iterate over all instances, computing the best move
565
        best_tbl = foldl' compareTables ini_tbl tables
566
        Table _ _ _ best_plc = best_tbl
567
    in if length best_plc == length ini_plc
550
  let Table _ _ _ ini_plc = ini_tbl
551
      -- we're using rwhnf from the Control.Parallel.Strategies
552
      -- package; we don't need to use rnf as that would force too
553
      -- much evaluation in single-threaded cases, and in
554
      -- multi-threaded case the weak head normal form is enough to
555
      -- spark the evaluation
556
      tables = parMap rwhnf (checkInstanceMove nodes_idx disk_moves
557
                             inst_moves ini_tbl)
558
               victims
559
      -- iterate over all instances, computing the best move
560
      best_tbl = foldl' compareTables ini_tbl tables
561
      Table _ _ _ best_plc = best_tbl
562
  in if length best_plc == length ini_plc
568 563
       then ini_tbl -- no advancement
569 564
       else best_tbl
570 565

  
......
574 569
              -> Score     -- ^ Score at which to stop
575 570
              -> Bool      -- ^ The resulting table and commands
576 571
doNextBalance ini_tbl max_rounds min_score =
577
    let Table _ _ ini_cv ini_plc = ini_tbl
578
        ini_plc_len = length ini_plc
579
    in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score
572
  let Table _ _ ini_cv ini_plc = ini_tbl
573
      ini_plc_len = length ini_plc
574
  in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score
580 575

  
581 576
-- | Run a balance move.
582 577
tryBalance :: Table       -- ^ The starting table

Also available in: Unified diff