commit 9cd96c47fdf6a1e4152ab26c39c0f87416f78319
Merge: f241763 f9139cd
Author: Klaus Aehlig <[email protected]>
Date:   Mon Jun 1 12:36:50 2015 +0200

    Merge branch 'stable-2.13' into stable-2.14
    
    * stable-2.13
      (no changes)
    
    * stable-2.14
      Make WConfD's updateLocksWaiting safe
      Tests specifying safeUpdateLocksWaiting
      Provide a repeatable version of updateLocksWaiting
      Verify that updateLocks is idempotent
      Always accept no-op requests
      Allow unconditional failovers off offline nodes
      Remove now unused variable
      Fix bug in ssconf comparison, disable it for vcluster
      QA: test renewing the cluster certificate only
      QA: Assert equality of ssconf_master_candidate_certs
      QA: Add more verify steps in renew crypto QA
    
    * stable-2.11
      (no changes)
    
    * stable-2.10
      Substitute 'suffix' for 'revision'
    
    Conflicts:
        src/Ganeti/HTools/Node.hs (apply condition at new code location)

diff --cc src/Ganeti/HTools/Node.hs
index e92df67,cb17f24..f47a45f
--- a/src/Ganeti/HTools/Node.hs
+++ b/src/Ganeti/HTools/Node.hs
@@@ -936,93 -608,52 +937,101 @@@ addSec = addSecEx Fals
  
  -- | Adds a secondary instance (extended version).
  addSecEx :: Bool -> Node -> Instance.Instance -> T.Ndx -> T.OpResult Node
- addSecEx force t inst pdx = -- trace "addSecEx" $
+ addSecEx = addSecExEx False
+ 
+ -- | Adds a secondary instance (doubly extended version). The first parameter
+ -- tells `addSecExEx` to ignore disks completly. There is only one legitimate
+ -- use case for this, and this is failing over a DRBD instance where the 
primary
+ -- node is offline (and hence will become the secondary afterwards).
+ addSecExEx :: Bool
+            -> Bool -> Node -> Instance.Instance -> T.Ndx -> T.OpResult Node
+ addSecExEx ignore_disks force t inst pdx =
    let iname = Instance.idx inst
 +      forthcoming = Instance.forthcoming inst
        old_peers = peers t
 -      old_mem = fMem t
 -      new_dsk = fDsk t - Instance.dsk inst
 -      new_free_sp = calcNewFreeSpindles True t inst
 -      new_inst_sp = calcSpindleUse True t inst
 +      strict = not force
 +
        secondary_needed_mem = if Instance.usesSecMem inst
                                 then Instance.mem inst
                                 else 0
        new_peem = P.find pdx old_peers + secondary_needed_mem
        new_peers = P.add pdx new_peem old_peers
 -      new_rmem = max (rMem t) new_peem
 -      new_prem = fromIntegral new_rmem / tMem t
 -      new_failn1 = old_mem <= new_rmem
 -      new_dp = computeNewPDsk t new_free_sp new_dsk
 -      old_load = utilLoad t
 -      new_load = old_load { T.dskWeight = T.dskWeight old_load +
 -                                          T.dskWeight (Instance.util inst) }
 -      strict = not force
 -  in case () of
 -       _ | not (Instance.hasSecondary inst) -> Bad T.FailDisk
 -         | not ignore_disks && new_dsk <= 0 -> Bad T.FailDisk
 -         | new_dsk < loDsk t && strict -> Bad T.FailDisk
 -         | exclStorage t && new_free_sp < 0 -> Bad T.FailSpindles
 -         | new_inst_sp > hiSpindles t && strict -> Bad T.FailDisk
 -         | secondary_needed_mem >= old_mem && strict -> Bad T.FailMem
 -         | new_failn1 && not (failN1 t) && strict -> Bad T.FailMem
 -         | otherwise ->
 -           let new_slist = iname:sList t
 -               r = t { sList = new_slist, fDsk = new_dsk
 -                     , peers = new_peers, failN1 = new_failn1
 -                     , rMem = new_rmem, pDsk = new_dp
 -                     , pRem = new_prem, utilLoad = new_load
 -                     , instSpindles = new_inst_sp
 -                     , fSpindles = new_free_sp
 -                     }
 -           in Ok r
 +
 +      old_mem_forth = fMemForth t
 +      new_dsk_forth = fDskForth t - Instance.dsk inst
 +      new_free_sp_forth = calcNewFreeSpindlesForth True t inst
 +      new_inst_sp_forth = calcSpindleUseForth True t inst
 +      new_dp_forth = computeNewPDsk t new_free_sp_forth new_dsk_forth
 +      old_load_forth = utilLoadForth t
 +      new_load_forth = old_load_forth
 +                         { T.dskWeight = T.dskWeight old_load_forth +
 +                                         T.dskWeight (Instance.util inst)
 +                         }
 +      new_slist_forth = iname:sListForth t
 +
 +      updateForthcomingFields n =
 +        n { sListForth = new_slist_forth
 +          , fDskForth = new_dsk_forth
 +          , pDskForth = new_dp_forth
 +          , utilLoadForth = new_load_forth
 +          , instSpindlesForth = new_inst_sp_forth
 +          , fSpindlesForth = new_free_sp_forth
 +
 +          -- TODO Set failN1Forth, rMemForth, pRemForth
 +          }
 +
 +      checkForthcomingViolation
 +        | not (Instance.hasSecondary inst)       = Bad T.FailDisk
 +        | new_dsk_forth <= 0                     = Bad T.FailDisk
 +        | new_dsk_forth < loDsk t                = Bad T.FailDisk
 +        | exclStorage t && new_free_sp_forth < 0 = Bad T.FailSpindles
 +        | new_inst_sp_forth > hiSpindles t       = Bad T.FailDisk
 +        | secondary_needed_mem >= old_mem_forth  = Bad T.FailMem
 +        -- TODO Check failN1 including forthcoming instances
 +        | otherwise                              = Ok ()
 +
 +  in if forthcoming
 +      then case strict of
 +             True | Bad err <- checkForthcomingViolation -> Bad err
 +
 +             _ -> Ok $ updateForthcomingFields t
 +      else let
 +               old_mem = fMem t
 +               new_dsk = fDsk t - Instance.dsk inst
 +               new_free_sp = calcNewFreeSpindles True t inst
 +               new_inst_sp = calcSpindleUse True t inst
 +               new_rmem = max (rMem t) new_peem
 +               new_prem = fromIntegral new_rmem / tMem t
 +               new_failn1 = old_mem <= new_rmem
 +               new_dp = computeNewPDsk t new_free_sp new_dsk
 +               old_load = utilLoad t
 +               new_load = old_load
 +                            { T.dskWeight = T.dskWeight old_load +
 +                                            T.dskWeight (Instance.util inst)
 +                            }
 +               new_slist = iname:sList t
 +      in case () of
 +        _ | not (Instance.hasSecondary inst) -> Bad T.FailDisk
-           | new_dsk <= 0 -> Bad T.FailDisk
++          | not ignore_disks && new_dsk <= 0 -> Bad T.FailDisk
 +          | strict && new_dsk < loDsk t -> Bad T.FailDisk
 +          | exclStorage t && new_free_sp < 0 -> Bad T.FailSpindles
 +          | strict && new_inst_sp > hiSpindles t -> Bad T.FailDisk
 +          | strict && secondary_needed_mem >= old_mem -> Bad T.FailMem
 +          | strict && new_failn1 && not (failN1 t) -> Bad T.FailMem
 +
 +          -- When strict also check forthcoming limits, but after normal 
checks
 +          | strict, Bad err <- checkForthcomingViolation -> Bad err
 +
 +          | otherwise ->
 +              Ok . updateForthcomingFields $
 +                t { sList = new_slist, fDsk = new_dsk
 +                  , peers = new_peers, failN1 = new_failn1
 +                  , rMem = new_rmem, pDsk = new_dp
 +                  , pRem = new_prem, utilLoad = new_load
 +                  , instSpindles = new_inst_sp
 +                  , fSpindles = new_free_sp
 +                  }
 +
  
  -- | Predicate on whether migration is supported between two nodes.
  checkMigration :: Node -> Node -> T.OpResult ()
diff --cc test/hs/Test/Ganeti/Locking/Waiting.hs
index 40ee0c5,3950663..de863b2
--- a/test/hs/Test/Ganeti/Locking/Waiting.hs
+++ b/test/hs/Test/Ganeti/Locking/Waiting.hs
@@@ -245,9 -244,27 +245,27 @@@ prop_PendingJustified 
    let isJustified (_, b, req) =
          genericResult (const False) (not . S.null) . snd
          . L.updateLocks b req $ getAllocation state
 -  in printTestCase "Pebding requests must be good and not fulfillable"
 +  in counterexample "Pending requests must be good and not fulfillable"
       . all isJustified . S.toList $ getPendingRequests state
  
+ -- | Verify that `updateLocks` is idempotent, except that in the repetition,
+ -- no waiters are notified.
+ prop_UpdateIdempotent :: Property
+ prop_UpdateIdempotent =
+   forAll (arbitrary :: Gen (LockWaiting TestLock TestOwner Integer)) $ \state 
->
+   forAll (arbitrary :: Gen TestOwner) $ \owner ->
+   forAll (arbitrary :: Gen [LockRequest TestLock]) $ \req ->
+   let (state', (answer', _)) = updateLocks owner req state
+       (state'', (answer'', nfy)) = updateLocks owner req state'
+   in conjoin [ printTestCase ("repeated updateLocks waiting gave different\
+                               \ answers: " ++ show answer' ++ " /= "
+                               ++ show answer'') $ answer' == answer''
+              , printTestCase "updateLocks not idempotent"
+                $ extRepr state' == extRepr state''
+              , printTestCase ("notifications (" ++ show nfy ++ ") on replay")
+                $ S.null nfy
+              ]
+ 
  -- | Verify that extRepr . fromExtRepr = id for all valid extensional
  -- representations.
  prop_extReprPreserved :: Property

-- 
Klaus Aehlig
Google Germany GmbH, Dienerstr. 12, 80331 Muenchen
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschaeftsfuehrer: Graham Law, Christine Elizabeth Flores

Reply via email to