module Ganeti.WConfd.ConfigWriter
( loadConfigFromFile
, readConfig
, writeConfig
, saveConfigAsyncTask
, distMCsAsyncTask
, distSSConfAsyncTask
) where
import Control.Applicative
import Control.Monad.Base
import Control.Monad.Error
import qualified Control.Monad.State.Strict as S
import Control.Monad.Trans.Control
import Data.Monoid
import Ganeti.BasicTypes
import Ganeti.Errors
import Ganeti.Config
import Ganeti.Logging
import Ganeti.Objects
import Ganeti.Rpc
import Ganeti.Runtime
import Ganeti.Utils
import Ganeti.Utils.Atomic
import Ganeti.Utils.AsyncWorker
import Ganeti.WConfd.ConfigState
import Ganeti.WConfd.Monad
import Ganeti.WConfd.Ssconf
loadConfigFromFile :: FilePath
-> ResultG (ConfigData, FStat)
loadConfigFromFile path = withLockedFile path $ \_ -> do
stat <- liftBase $ getFStat path
cd <- mkResultT (loadConfig path)
return (cd, stat)
writeConfigToFile :: (MonadBase IO m, MonadError GanetiException m, MonadLog m)
=> ConfigData -> FilePath -> FStat -> m FStat
writeConfigToFile cfg path oldstat = do
logDebug $ "Async. config. writer: Commencing write\
\ serial no " ++ show (serialOf cfg)
r <- toErrorBase $ atomicUpdateLockedFile_ path oldstat doWrite
logDebug "Async. config. writer: written"
return r
where
doWrite fname fh = do
setOwnerAndGroupFromNames fname GanetiWConfd
(DaemonGroup GanetiConfd)
setOwnerWGroupR fname
saveConfig fh cfg
readConfig :: WConfdMonad ConfigData
readConfig = csConfigData <$> readConfigState
writeConfig :: ConfigData -> WConfdMonad ()
writeConfig cd = modifyConfigState $ const ((), mkConfigState cd)
finishOrLog :: (Show e, MonadLog m)
=> Priority
-> String
-> (a -> m ())
-> GenericResult e a
-> m ()
finishOrLog logPrio logPrefix =
genericResult (logAt logPrio . (++) (logPrefix ++ ": ") . show)
mkStatelessAsyncTask :: (MonadBaseControl IO m, MonadLog m, Show e, Monoid i)
=> Priority
-> String
-> (i -> ResultT e m ())
-> m (AsyncWorker i ())
mkStatelessAsyncTask logPrio logPrefix action =
mkAsyncWorker $ runResultT . action
>=> finishOrLog logPrio logPrefix return
mkStatefulAsyncTask :: (MonadBaseControl IO m, MonadLog m, Show e, Monoid i)
=> Priority
-> String
-> s
-> (s -> i -> ResultT e m s)
-> m (AsyncWorker i ())
mkStatefulAsyncTask logPrio logPrefix start action =
flip S.evalStateT start . mkAsyncWorker $ \i ->
S.get >>= lift . runResultT . flip action i
>>= finishOrLog logPrio logPrefix S.put
saveConfigAsyncTask :: FilePath
-> FStat
-> IO ConfigState
-> [AsyncWorker () ()]
-> ResultG (AsyncWorker Any ())
saveConfigAsyncTask fpath fstat cdRef workers =
lift . mkStatefulAsyncTask
EMERGENCY "Can't write the master configuration file" fstat
$ \oldstat (Any flush) -> do
cd <- liftBase (csConfigData `liftM` cdRef)
writeConfigToFile cd fpath oldstat
<* if flush then logDebug "Running distribution synchronously"
>> triggerAndWaitMany_ workers
else logDebug "Running distribution asynchronously"
>> mapM trigger_ workers
execRpcCallAndLog :: (Rpc a b) => [Node] -> a -> ResultG ()
execRpcCallAndLog nodes req = do
rs <- liftIO $ executeRpcCall nodes req
es <- logRpcErrors rs
unless (null es) $ failError "At least one of the RPC calls failed"
distMCsAsyncTask :: RuntimeEnts
-> FilePath
-> IO ConfigState
-> ResultG (AsyncWorker () ())
distMCsAsyncTask ents cpath cdRef =
lift . mkStatelessAsyncTask ERROR "Can't distribute the configuration\
\ to master candidates"
$ \_ -> do
cd <- liftBase (csConfigData <$> cdRef) :: ResultG ConfigData
logDebug $ "Distributing the configuration to master candidates,\
\ serial no " ++ show (serialOf cd)
fupload <- prepareRpcCallUploadFile ents cpath
execRpcCallAndLog (getMasterCandidates cd) fupload
logDebug "Successfully finished distributing the configuration"
distSSConfAsyncTask
:: IO ConfigState
-> ResultG (AsyncWorker () ())
distSSConfAsyncTask cdRef =
lift . mkStatefulAsyncTask ERROR "Can't distribute Ssconf" emptySSConf
$ \oldssc _ -> do
cd <- liftBase (csConfigData <$> cdRef) :: ResultG ConfigData
let ssc = mkSSConf cd
if oldssc == ssc
then logDebug "SSConf unchanged, not distributing"
else do
logDebug $ "Starting the distribution of SSConf\
\ serial no " ++ show (serialOf cd)
execRpcCallAndLog (getOnlineNodes cd)
(RpcCallWriteSsconfFiles ssc)
logDebug "Successfully finished distributing SSConf"
return ssc