module Ganeti.WConfd.Persistent
( Persistent(..)
, writePersistentAsyncTask
, readPersistent
, persistentLocks
, persistentTempRes
) where
import Control.Monad.Error
import System.Directory (doesFileExist)
import qualified Text.JSON as J
import Ganeti.BasicTypes
import Ganeti.Errors
import qualified Ganeti.JSON as J
import Ganeti.Locking.Waiting (emptyWaiting)
import Ganeti.Locking.Locks (ClientId(..), GanetiLockWaiting)
import Ganeti.Logging
import qualified Ganeti.Path as Path
import Ganeti.WConfd.Core (freeLocks, dropAllReservations)
import Ganeti.WConfd.Monad
import Ganeti.WConfd.TempRes (TempResState, emptyTempResState)
import Ganeti.Utils.Atomic
import Ganeti.Utils.AsyncWorker
data Persistent a = Persistent
{ persName :: String
, persPath :: IO FilePath
, persEmpty :: a
, persCleanup :: ClientId -> WConfdMonad ()
}
writePersistentAsyncTask
:: (J.JSON a) => Persistent a -> IO a -> ResultG (AsyncWorker () ())
writePersistentAsyncTask pers readAction = mkAsyncWorker_ $
catchError (do
let prefix = "Async. " ++ persName pers ++ " writer: "
fpath <- liftIO $ persPath pers
logDebug $ prefix ++ "Starting write to " ++ fpath
state <- liftIO readAction
toErrorBase . liftIO . atomicWriteFile fpath . J.encode $ state
logDebug $ prefix ++ "written"
) (logEmergency . (++) ("Can't write " ++ persName pers ++ " state: ")
. show)
readPersistent :: (J.JSON a) => Persistent a -> ResultG a
readPersistent pers = do
logDebug $ "Reading " ++ persName pers
file <- liftIO $ persPath pers
file_present <- liftIO $ doesFileExist file
if file_present
then
liftIO (persPath pers >>= readFile)
>>= J.fromJResultE ("parsing " ++ persName pers) . J.decodeStrict
else do
logInfo $ "Note: No saved data for " ++ persName pers
++ ", tacitly assuming empty."
return (persEmpty pers)
persistentLocks :: Persistent GanetiLockWaiting
persistentLocks = Persistent
{ persName = "lock allocation state"
, persPath = Path.lockStatusFile
, persEmpty = emptyWaiting
, persCleanup = freeLocks
}
persistentTempRes :: Persistent TempResState
persistentTempRes = Persistent
{ persName = "temporary reservations"
, persPath = Path.tempResStatusFile
, persEmpty = emptyTempResState
, persCleanup = dropAllReservations
}