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