Commit 0c26d1ed authored by Klaus Aehlig's avatar Klaus Aehlig

Add an RPC to prepare cluster destruction

To avoid subtle races when destroying a cluster, we need to make
sure that the cluster destroy LU is the last LU ever executed.
That LU gets the BGL exclusively; however, it needs the daemons
for it to proceed, so the daemons will still be running at its
finish, thus causing the race. (Also note, that the watcher might
restart stopped daemons at any time.) Therefore, the last thing
that LU will do is to transfer the BGL to WConfD itself; WConfD
will also modify the configuration to a no-master state, thus
making sure it will refuse to restart.
Signed-off-by: default avatarKlaus Aehlig <>
Reviewed-by: default avatarPetr Pudlak <>
parent b7c7e52b
module Ganeti.WConfd.Core where
import Control.Arrow ((&&&))
import Control.Concurrent (myThreadId)
import Control.Lens.Setter (set)
import Control.Monad (liftM, unless, when)
import qualified Data.Map as M
import qualified Data.Set as S
import Language.Haskell.TH (Name)
import System.Posix.Process (getProcessID)
import qualified System.Random as Rand
import Ganeti.BasicTypes
import qualified Ganeti.Constants as C
import qualified Ganeti.JSON as J
import qualified Ganeti.Locking.Allocation as L
import Ganeti.Locking.Locks ( GanetiLocks(ConfigLock), LockLevel(LevelConfig)
, lockLevel, LockLevel, ClientId )
import Ganeti.Logging (logDebug)
import Ganeti.Locking.Locks ( GanetiLocks(ConfigLock, BGL)
, LockLevel(LevelConfig)
, lockLevel, LockLevel
, ClientType(ClientOther), ClientId(..) )
import qualified Ganeti.Locking.Waiting as LW
import Ganeti.Objects (ConfigData, DRBDSecret, LogicalVolume, Ip4Address)
import Ganeti.Objects.Lens (configClusterL, clusterMasterNodeL)
import Ganeti.WConfd.ConfigState (csConfigDataL)
import qualified Ganeti.WConfd.ConfigVerify as V
import Ganeti.WConfd.Language
import Ganeti.WConfd.Monad
......@@ -309,10 +318,44 @@ guardedOpportunisticLockUnion :: Int
guardedOpportunisticLockUnion count cid req =
modifyLockWaiting $ LW.guardedOpportunisticLockUnion count cid req
-- * Prepareation for cluster destruction
-- | Prepare daemon for cluster destruction. This consists of
-- verifying that the requester owns the BGL exclusively, transfering the BGL
-- to WConfD itself, and modifying the configuration so that no
-- node is the master any more. Note that, since we own the BGL exclusively,
-- we can safely modify the configuration, as no other process can request
-- changes.
prepareClusterDestruction :: ClientId -> WConfdMonad ()
prepareClusterDestruction cid = do
la <- readLockAllocation
unless (L.holdsLock cid BGL L.OwnExclusive la)
. failError $ "Cluster destruction requested without owning BGL exclusively"
logDebug $ "preparing cluster destruction as requested by " ++ show cid
-- transfer BGL to ourselfs. The do this, by adding a super-priority waiting
-- request and then releasing the BGL of the requestor.
dh <- daemonHandle
pid <- liftIO getProcessID
tid <- liftIO myThreadId
let mycid = ClientId { ciIdentifier = ClientOther $ "wconfd-" ++ show tid
, ciLockFile = dhLivelock dh
, ciPid = pid
_ <- modifyLockWaiting $ LW.updateLocksWaiting
(fromIntegral C.opPrioHighest - 1) mycid
[L.requestExclusive BGL]
_ <- modifyLockWaiting $ LW.updateLocks cid [L.requestRelease BGL]
-- To avoid beeing restarted we change the configuration to a no-master
-- state.
modifyConfigState $ (,) ()
. set (csConfigDataL . configClusterL . clusterMasterNodeL) ""
-- * The list of all functions exported to RPC.
exportedFunctions :: [Name]
exportedFunctions = [ 'echo
, 'prepareClusterDestruction
-- config
, 'readConfig
, 'writeConfig
......@@ -74,6 +74,9 @@ class MockClient(object):
self.wconfdmock.mylocks[lockrq[0]] = lockrq[1]
return [lockrq[0] for lockrq in req]
def PrepareClusterDestruction(self, _cid):
class WConfdMock(object):
"""Mock calls to WConfD.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment