Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / WConfd / Monad.hs @ 41ea331e

History | View | Annotate | Download (7.3 kB)

1
{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleContexts #-}
2
{-# LANGUAGE TemplateHaskell #-}
3

    
4
{-| All RPC calls are run within this monad.
5

    
6
It encapsulates:
7

    
8
* IO operations,
9
* failures,
10
* working with the daemon state.
11

    
12
Code that is specific either to the configuration or the lock management, should
13
go into their corresponding dedicated modules.
14
-}
15

    
16
{-
17

    
18
Copyright (C) 2014 Google Inc.
19

    
20
This program is free software; you can redistribute it and/or modify
21
it under the terms of the GNU General Public License as published by
22
the Free Software Foundation; either version 2 of the License, or
23
(at your option) any later version.
24

    
25
This program is distributed in the hope that it will be useful, but
26
WITHOUT ANY WARRANTY; without even the implied warranty of
27
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28
General Public License for more details.
29

    
30
You should have received a copy of the GNU General Public License
31
along with this program; if not, write to the Free Software
32
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33
02110-1301, USA.
34

    
35
-}
36

    
37
module Ganeti.WConfd.Monad
38
  ( DaemonHandle
39
  , dhConfigPath
40
  , mkDaemonHandle
41
  , WConfdMonadInt
42
  , runWConfdMonadInt
43
  , WConfdMonad
44
  , daemonHandle
45
  , modifyConfigState
46
  , readConfigState
47
  , modifyLockAllocation
48
  , modifyLockAllocation_
49
  , readLockAllocation
50
  ) where
51

    
52
import Control.Applicative
53
import Control.Monad
54
import Control.Monad.Base
55
import Control.Monad.Error
56
import Control.Monad.Reader
57
import Control.Monad.Trans.Control
58
import Data.IORef.Lifted
59
import Data.Tuple (swap)
60

    
61
import Ganeti.BasicTypes
62
import Ganeti.Errors
63
import Ganeti.Lens
64
import Ganeti.Locking.Locks
65
import Ganeti.Logging
66
import Ganeti.Utils.AsyncWorker
67
import Ganeti.WConfd.ConfigState
68

    
69
-- * Pure data types used in the monad
70

    
71
-- | The state of the daemon, capturing both the configuration state and the
72
-- locking state.
73
data DaemonState = DaemonState
74
  { dsConfigState :: ConfigState
75
  , dsLockAllocation :: GanetiLockAllocation
76
  }
77

    
78
$(makeCustomLenses ''DaemonState)
79

    
80
data DaemonHandle = DaemonHandle
81
  { dhDaemonState :: IORef DaemonState -- ^ The current state of the daemon
82
  , dhConfigPath :: FilePath           -- ^ The configuration file path
83
  -- all static information that doesn't change during the life-time of the
84
  -- daemon should go here;
85
  -- all IDs of threads that do asynchronous work should probably also go here
86
  , dhSaveConfigWorker :: AsyncWorker ()
87
  , dhDistMCsWorker :: AsyncWorker ()
88
  , dhSaveLocksWorker :: AsyncWorker ()
89
  }
90

    
91
mkDaemonHandle :: FilePath
92
               -> ConfigState
93
               -> GanetiLockAllocation
94
               -> (IO ConfigState -> ResultG (AsyncWorker ()))
95
                  -- ^ A function that creates a worker that asynchronously
96
                  -- saves the configuration to the master file.
97
               -> (IO ConfigState -> ResultG (AsyncWorker ()))
98
                  -- ^ A function that creates a worker that asynchronously
99
                  -- distributes the configuration to master candidates
100
               -> (IO GanetiLockAllocation -> ResultG (AsyncWorker ()))
101
                  -- ^ A function that creates a worker that asynchronously
102
                  -- saves the lock allocation state.
103
               -> ResultG DaemonHandle
104
mkDaemonHandle cpath cstat lstat
105
               saveWorkerFn distMCsWorkerFn
106
               saveLockWorkerFn = do
107
  ds <- newIORef $ DaemonState cstat lstat
108
  let readConfigIO = dsConfigState `liftM` readIORef ds :: IO ConfigState
109

    
110
  saveWorker <- saveWorkerFn readConfigIO
111
  distMCsWorker <- distMCsWorkerFn readConfigIO
112

    
113
  saveLockWorker <- saveLockWorkerFn $ dsLockAllocation `liftM` readIORef ds
114

    
115
  return $ DaemonHandle ds cpath saveWorker distMCsWorker
116
                                 saveLockWorker
117

    
118
-- * The monad and its instances
119

    
120
-- | A type alias for easier referring to the actual content of the monad
121
-- when implementing its instances.
122
type WConfdMonadIntType = ReaderT DaemonHandle IO
123

    
124
-- | The internal part of the monad without error handling.
125
newtype WConfdMonadInt a = WConfdMonadInt
126
  { getWConfdMonadInt :: WConfdMonadIntType a }
127

    
128
instance Functor WConfdMonadInt where
129
  fmap f = WConfdMonadInt . fmap f . getWConfdMonadInt
130

    
131
instance Applicative WConfdMonadInt where
132
  pure = WConfdMonadInt . pure
133
  WConfdMonadInt f <*> WConfdMonadInt k = WConfdMonadInt $ f <*> k
134

    
135
instance Monad WConfdMonadInt where
136
  return = WConfdMonadInt . return
137
  (WConfdMonadInt k) >>= f = WConfdMonadInt $ k >>= getWConfdMonadInt . f
138

    
139
instance MonadIO WConfdMonadInt where
140
  liftIO = WConfdMonadInt . liftIO
141

    
142
instance MonadBase IO WConfdMonadInt where
143
  liftBase = WConfdMonadInt . liftBase
144

    
145
instance MonadBaseControl IO WConfdMonadInt where
146
  newtype StM WConfdMonadInt b = StMWConfdMonadInt
147
    { runStMWConfdMonadInt :: StM WConfdMonadIntType b }
148
  liftBaseWith f = WConfdMonadInt . liftBaseWith
149
                   $ \r -> f (liftM StMWConfdMonadInt . r . getWConfdMonadInt)
150
  restoreM = WConfdMonadInt . restoreM . runStMWConfdMonadInt
151

    
152
instance MonadLog WConfdMonadInt where
153
  logAt p = WConfdMonadInt . logAt p
154

    
155
-- | Runs the internal part of the WConfdMonad monad on a given daemon
156
-- handle.
157
runWConfdMonadInt :: WConfdMonadInt a -> DaemonHandle -> IO a
158
runWConfdMonadInt (WConfdMonadInt k) = runReaderT k
159

    
160
-- | The complete monad with error handling.
161
type WConfdMonad = ResultT GanetiException WConfdMonadInt
162

    
163
-- * Basic functions in the monad
164

    
165
-- | Returns the daemon handle.
166
daemonHandle :: WConfdMonad DaemonHandle
167
daemonHandle = lift . WConfdMonadInt $ ask
168

    
169
-- | Returns the current configuration, given a handle
170
readConfigState :: WConfdMonad ConfigState
171
readConfigState = liftM dsConfigState . readIORef . dhDaemonState
172
                  =<< daemonHandle
173

    
174
-- | Atomically modifies the configuration state in the WConfdMonad.
175
modifyConfigState :: (ConfigState -> (ConfigState, a)) -> WConfdMonad a
176
modifyConfigState f = do
177
  dh <- daemonHandle
178
  let modCS cs = let (cs', r) = f cs
179
                  in ((r, cs /= cs'), cs')
180
  let mf = traverseOf dsConfigStateL modCS
181
  (r, modified) <- atomicModifyIORef (dhDaemonState dh) (swap . mf)
182
  when modified $ do
183
    -- trigger the config. saving worker and wait for it
184
    logDebug "Triggering config write"
185
    liftBase . triggerAndWait . dhSaveConfigWorker $ dh
186
    logDebug "Config write finished"
187
    -- trigger the config. distribution worker asynchronously
188
    liftBase . triggerAndWait . dhDistMCsWorker $ dh
189
    -- TODO: SSConf
190
  return r
191

    
192
-- | Atomically modifies the lock allocation state in WConfdMonad.
193
modifyLockAllocation :: (GanetiLockAllocation -> (GanetiLockAllocation, a))
194
                     -> WConfdMonad a
195
modifyLockAllocation f = do
196
  dh <- lift . WConfdMonadInt $ ask
197
  r <- atomicModifyIORef (dhDaemonState dh)
198
                         (swap . traverseOf dsLockAllocationL (swap . f))
199
  logDebug "Triggering lock state write"
200
  liftBase . triggerAndWait . dhSaveLocksWorker $ dh
201
  logDebug "Lock write finished"
202
  return r
203

    
204
-- | Atomically modifies the lock allocation state in WConfdMonad, not
205
-- producing any result
206
modifyLockAllocation_ :: (GanetiLockAllocation -> GanetiLockAllocation)
207
                      -> WConfdMonad ()
208
modifyLockAllocation_ = modifyLockAllocation . (flip (,) () .)
209

    
210
-- | Read the lock allocation state.
211
readLockAllocation :: WConfdMonad GanetiLockAllocation
212
readLockAllocation = liftM dsLockAllocation . readIORef . dhDaemonState
213
                     =<< daemonHandle