Revision 74ff6aed

b/src/Ganeti/HTools/Node.hs
353 353
  in t {peers=pmap, failN1 = new_failN1, rMem = new_rmem, pRem = new_prem}
354 354

  
355 355
-- | Calculate the new spindle usage
356
calcSpindleUse :: Node -> Instance.Instance -> Double
357
calcSpindleUse n i = incIf (Instance.usesLocalStorage i) (instSpindles n)
358
                       (fromIntegral $ Instance.spindleUse i)
356
calcSpindleUse ::
357
                  Bool -- Action: True = adding instance, False = removing it
358
               -> Node -> Instance.Instance -> Double
359
calcSpindleUse _ (Node {exclStorage = True}) _ = 0.0
360
calcSpindleUse act n@(Node {exclStorage = False}) i =
361
  f (Instance.usesLocalStorage i) (instSpindles n)
362
    (fromIntegral $ Instance.spindleUse i)
363
    where
364
      f :: Bool -> Double -> Double -> Double -- avoid monomorphism restriction
365
      f = if act then incIf else decIf
366

  
367
-- | Calculate the new number of free spindles
368
calcNewFreeSpindles ::
369
                       Bool -- Action: True = adding instance, False = removing
370
                    -> Node -> Instance.Instance -> Int
371
calcNewFreeSpindles _ (Node {exclStorage = False}) _ = 0
372
calcNewFreeSpindles act n@(Node {exclStorage = True}) i =
373
  case Instance.getTotalSpindles i of
374
    Nothing -> if act
375
               then -1 -- Force a spindle error, so the instance don't go here
376
               else fSpindles n -- No change, as we aren't sure
377
    Just s -> (if act then (-) else (+)) (fSpindles n) s
359 378

  
360 379
-- | Assigns an instance to a node as primary and update the used VCPU
361 380
-- count, utilisation data and tags map.
......
365 384
                  , pCpu = fromIntegral new_count / tCpu t
366 385
                  , utilLoad = utilLoad t `T.addUtil` Instance.util inst
367 386
                  , pTags = addTags (pTags t) (Instance.exclTags inst)
368
                  , instSpindles = calcSpindleUse t inst
387
                  , instSpindles = calcSpindleUse True t inst
369 388
                  }
370 389
  where new_count = Instance.applyIfOnline inst (+ Instance.vcpus inst)
371 390
                    (uCpu t )
372 391

  
373
-- | Assigns an instance to a node as secondary without other updates.
392
-- | Assigns an instance to a node as secondary and updates disk utilisation.
374 393
setSec :: Node -> Instance.Instance -> Node
375 394
setSec t inst = t { sList = Instance.idx inst:sList t
376 395
                  , utilLoad = old_load { T.dskWeight = T.dskWeight old_load +
377 396
                                          T.dskWeight (Instance.util inst) }
378
                  , instSpindles = calcSpindleUse t inst
397
                  , instSpindles = calcSpindleUse True t inst
379 398
                  }
380 399
  where old_load = utilLoad t
381 400

  
......
403 422
      new_plist = delete iname (pList t)
404 423
      new_mem = incIf i_online (fMem t) (Instance.mem inst)
405 424
      new_dsk = incIf uses_disk (fDsk t) (Instance.dsk inst)
406
      new_spindles = decIf uses_disk (instSpindles t) 1
425
      new_free_sp = calcNewFreeSpindles False t inst
426
      new_inst_sp = calcSpindleUse False t inst
407 427
      new_mp = fromIntegral new_mem / tMem t
408 428
      new_dp = computePDsk new_dsk (tDsk t)
409 429
      new_failn1 = new_mem <= rMem t
......
414 434
       , failN1 = new_failn1, pMem = new_mp, pDsk = new_dp
415 435
       , uCpu = new_ucpu, pCpu = new_rcpu, utilLoad = new_load
416 436
       , pTags = delTags (pTags t) (Instance.exclTags inst)
417
       , instSpindles = new_spindles
437
       , instSpindles = new_inst_sp, fSpindles = new_free_sp
418 438
       }
419 439

  
420 440
-- | Removes a secondary instance.
......
426 446
      pnode = Instance.pNode inst
427 447
      new_slist = delete iname (sList t)
428 448
      new_dsk = incIf uses_disk cur_dsk (Instance.dsk inst)
429
      new_spindles = decIf uses_disk (instSpindles t) 1
449
      new_free_sp = calcNewFreeSpindles False t inst
450
      new_inst_sp = calcSpindleUse False t inst
430 451
      old_peers = peers t
431 452
      old_peem = P.find pnode old_peers
432 453
      new_peem = decIf (Instance.usesSecMem inst) old_peem (Instance.mem inst)
......
446 467
  in t { sList = new_slist, fDsk = new_dsk, peers = new_peers
447 468
       , failN1 = new_failn1, rMem = new_rmem, pDsk = new_dp
448 469
       , pRem = new_prem, utilLoad = new_load
449
       , instSpindles = new_spindles
470
       , instSpindles = new_inst_sp, fSpindles = new_free_sp
450 471
       }
451 472

  
452 473
-- | Adds a primary instance (basic version).
......
470 491
      cur_dsk = fDsk t
471 492
      new_mem = decIf i_online (fMem t) (Instance.mem inst)
472 493
      new_dsk = decIf uses_disk cur_dsk (Instance.dsk inst)
473
      new_spindles = incIf uses_disk (instSpindles t) 1
494
      new_free_sp = calcNewFreeSpindles True t inst
495
      new_inst_sp = calcSpindleUse True t inst
474 496
      new_failn1 = new_mem <= rMem t
475 497
      new_ucpu = incIf i_online (uCpu t) (Instance.vcpus inst)
476 498
      new_pcpu = fromIntegral new_ucpu / tCpu t
......
484 506
       _ | new_mem <= 0 -> Bad T.FailMem
485 507
         | uses_disk && new_dsk <= 0 -> Bad T.FailDisk
486 508
         | uses_disk && mDsk t > new_dp && strict -> Bad T.FailDisk
487
         | uses_disk && new_spindles > hiSpindles t
488
             && strict -> Bad T.FailDisk
509
         | uses_disk && exclStorage t && new_free_sp < 0 -> Bad T.FailSpindles
510
         | uses_disk && new_inst_sp > hiSpindles t && strict -> Bad T.FailDisk
489 511
         | new_failn1 && not (failN1 t) && strict -> Bad T.FailMem
490 512
         | l_cpu >= 0 && l_cpu < new_pcpu && strict -> Bad T.FailCPU
491 513
         | rejectAddTags old_tags inst_tags -> Bad T.FailTags
......
497 519
                     , uCpu = new_ucpu, pCpu = new_pcpu
498 520
                     , utilLoad = new_load
499 521
                     , pTags = addTags old_tags inst_tags
500
                     , instSpindles = new_spindles
522
                     , instSpindles = new_inst_sp
523
                     , fSpindles = new_free_sp
501 524
                     }
502 525
           in Ok r
503 526

  
......
512 535
      old_peers = peers t
513 536
      old_mem = fMem t
514 537
      new_dsk = fDsk t - Instance.dsk inst
515
      new_spindles = instSpindles t + 1
538
      new_free_sp = calcNewFreeSpindles True t inst
539
      new_inst_sp = calcSpindleUse True t inst
516 540
      secondary_needed_mem = if Instance.usesSecMem inst
517 541
                               then Instance.mem inst
518 542
                               else 0
......
530 554
       _ | not (Instance.hasSecondary inst) -> Bad T.FailDisk
531 555
         | new_dsk <= 0 -> Bad T.FailDisk
532 556
         | mDsk t > new_dp && strict -> Bad T.FailDisk
533
         | new_spindles > hiSpindles t && strict -> Bad T.FailDisk
557
         | exclStorage t && new_free_sp < 0 -> Bad T.FailSpindles
558
         | new_inst_sp > hiSpindles t && strict -> Bad T.FailDisk
534 559
         | secondary_needed_mem >= old_mem && strict -> Bad T.FailMem
535 560
         | new_failn1 && not (failN1 t) && strict -> Bad T.FailMem
536 561
         | otherwise ->
......
539 564
                     , peers = new_peers, failN1 = new_failn1
540 565
                     , rMem = new_rmem, pDsk = new_dp
541 566
                     , pRem = new_prem, utilLoad = new_load
542
                     , instSpindles = new_spindles
567
                     , instSpindles = new_inst_sp
568
                     , fSpindles = new_free_sp
543 569
                     }
544 570
           in Ok r
545 571

  
b/test/hs/Test/Ganeti/HTools/Node.hs
214 214
                     , Instance.diskTemplate = dt }
215 215
  in (Node.addPri node inst'' ==? Bad Types.FailDisk)
216 216

  
217
-- | Check if an instance exceeds a spindles limit or has no spindles set.
218
hasInstTooManySpindles :: Instance.Instance -> Int -> Bool
219
hasInstTooManySpindles inst sp_lim =
220
  case Instance.getTotalSpindles inst of
221
    Just s -> s > sp_lim
222
    Nothing -> True
223

  
224
-- | Check that adding a primary instance with too many spindles fails
225
-- with type FailSpindles (when exclusive storage is enabled).
226
prop_addPriFS :: Instance.Instance -> Property
227
prop_addPriFS inst =
228
  forAll genExclStorNode $ \node ->
229
  forAll (elements Instance.localStorageTemplates) $ \dt ->
230
  hasInstTooManySpindles inst (Node.fSpindles node) &&
231
    not (Node.failN1 node) ==>
232
  let inst' = setInstanceSmallerThanNode node inst
233
      inst'' = inst' { Instance.disks = Instance.disks inst
234
                     , Instance.diskTemplate = dt }
235
  in (Node.addPri node inst'' ==? Bad Types.FailSpindles)
236

  
217 237
-- | Check that adding a primary instance with too many VCPUs fails
218 238
-- with type FailCPU.
219 239
prop_addPriFC :: Property
......
233 253
prop_addSec node inst pdx =
234 254
  ((Instance.mem inst >= (Node.fMem node - Node.rMem node) &&
235 255
    not (Instance.isOffline inst)) ||
236
   Instance.dsk inst >= Node.fDsk node) &&
256
   Instance.dsk inst >= Node.fDsk node ||
257
   (Node.exclStorage node &&
258
    hasInstTooManySpindles inst (Node.fSpindles node))) &&
237 259
  not (Node.failN1 node) ==>
238 260
      isBad (Node.addSec node inst pdx)
239 261

  
......
419 441
            , 'prop_setXmem
420 442
            , 'prop_addPriFM
421 443
            , 'prop_addPriFD
444
            , 'prop_addPriFS
422 445
            , 'prop_addPriFC
423 446
            , 'prop_addPri_NoN1Fail
424 447
            , 'prop_addSec
b/test/hs/shelltests/htools-hail.test
88 88
>>>= 0
89 89

  
90 90
# Same tests with exclusive storage enabled, but no spindles info in instances
91
# Operations involving relocation succed because policies are not checked
92 91
./test/hs/hail $T/hail-alloc-drbd.json.fail-excl-stor
93 92
>>> /"success":false,.*FailSpindles: 12"/
94 93
>>>= 0
95 94

  
96 95
./test/hs/hail $T/hail-reloc-drbd.json.fail-excl-stor
97
>>> /"success":true,.*,"result":\["node1"\]/
96
>>> /"success":false,.*FailSpindles/
98 97
>>>= 0
99 98

  
100 99
./test/hs/hail $T/hail-node-evac.json.fail-excl-stor
101
>>> /"success":true,"info":"Request successful: 0 instances failed to move and 1 were moved successfully"/
100
>>> /"success":true,"info":"Request successful: 1 instances failed to move and 0 were moved successfully",.*FailSpindles/
102 101
>>>= 0
103 102

  
104 103
./test/hs/hail $T/hail-change-group.json.fail-excl-stor
......
153 152
>>>= 0
154 153

  
155 154
./test/hs/hail $T/hail-alloc-spindles.json.excl-stor
156
>>> /"success":true,"info":"Request successful: Selected group: group1,.*FailSpindles: 2",.*"result":\["node2"\]/
155
>>> /"success":true,"info":"Request successful: Selected group: group1,.*FailSpindles: 2",.*"result":\["node1"\]/
157 156
>>>= 0

Also available in: Unified diff