root / src / Ganeti / WConfd / ConfigWriter.hs @ 94c7e022
History | View | Annotate | Download (6.1 kB)
1 |
{-# LANGUAGE RankNTypes, FlexibleContexts #-} |
---|---|
2 |
|
3 |
{-| Implementation of functions specific to configuration management. |
4 |
|
5 |
TODO: Detect changes in SSConf and distribute only if it changes |
6 |
TODO: distribute ssconf configuration, if it has changed |
7 |
|
8 |
-} |
9 |
|
10 |
{- |
11 |
|
12 |
Copyright (C) 2013 Google Inc. |
13 |
|
14 |
This program is free software; you can redistribute it and/or modify |
15 |
it under the terms of the GNU General Public License as published by |
16 |
the Free Software Foundation; either version 2 of the License, or |
17 |
(at your option) any later version. |
18 |
|
19 |
This program is distributed in the hope that it will be useful, but |
20 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 |
General Public License for more details. |
23 |
|
24 |
You should have received a copy of the GNU General Public License |
25 |
along with this program; if not, write to the Free Software |
26 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
27 |
02110-1301, USA. |
28 |
|
29 |
-} |
30 |
|
31 |
module Ganeti.WConfd.ConfigWriter |
32 |
( loadConfigFromFile |
33 |
, readConfig |
34 |
, writeConfig |
35 |
, saveConfigAsyncTask |
36 |
, distMCsAsyncTask |
37 |
) where |
38 |
|
39 |
import Control.Applicative |
40 |
import Control.Monad.Base |
41 |
import Control.Monad.Error |
42 |
import qualified Control.Monad.State.Strict as S |
43 |
import Control.Monad.Trans.Control |
44 |
|
45 |
import Ganeti.BasicTypes |
46 |
import Ganeti.Errors |
47 |
import Ganeti.Config |
48 |
import Ganeti.Logging |
49 |
import Ganeti.Objects |
50 |
import Ganeti.Rpc |
51 |
import Ganeti.Runtime |
52 |
import Ganeti.Utils |
53 |
import Ganeti.Utils.Atomic |
54 |
import Ganeti.Utils.AsyncWorker |
55 |
import Ganeti.WConfd.ConfigState |
56 |
import Ganeti.WConfd.Monad |
57 |
|
58 |
-- | Loads the configuration from the file, if it hasn't been loaded yet. |
59 |
-- The function is internal and isn't thread safe. |
60 |
loadConfigFromFile :: FilePath |
61 |
-> ResultG (ConfigData, FStat) |
62 |
loadConfigFromFile path = withLockedFile path $ \_ -> do |
63 |
stat <- liftBase $ getFStat path |
64 |
cd <- mkResultT (loadConfig path) |
65 |
return (cd, stat) |
66 |
|
67 |
-- | Writes the current configuration to the file. The function isn't thread |
68 |
-- safe. |
69 |
-- Neither distributes the configuration (to nodes and ssconf) nor |
70 |
-- updates the serial number. |
71 |
writeConfigToFile :: (MonadBase IO m, MonadError GanetiException m, MonadLog m) |
72 |
=> ConfigData -> FilePath -> FStat -> m FStat |
73 |
writeConfigToFile cfg path oldstat = do |
74 |
logDebug "Async. config. writer: Commencing write" |
75 |
r <- toErrorBase $ atomicUpdateLockedFile_ path oldstat doWrite |
76 |
logDebug "Async. config. writer: written" |
77 |
return r |
78 |
where |
79 |
doWrite fname fh = do |
80 |
setOwnerAndGroupFromNames fname GanetiWConfd |
81 |
(DaemonGroup GanetiConfd) |
82 |
setOwnerWGroupR fname |
83 |
saveConfig fh cfg |
84 |
|
85 |
-- Reads the current configuration state in the 'WConfdMonad'. |
86 |
readConfig :: WConfdMonad ConfigData |
87 |
readConfig = csConfigData <$> readConfigState |
88 |
|
89 |
-- Replaces the current configuration state within the 'WConfdMonad'. |
90 |
writeConfig :: ConfigData -> WConfdMonad () |
91 |
writeConfig cd = modifyConfigState $ const (mkConfigState cd, ()) |
92 |
|
93 |
-- * Asynchronous tasks |
94 |
|
95 |
-- | Runs the given action on success, or logs an error on failure. |
96 |
finishOrLog :: (Show e, MonadLog m) |
97 |
=> Priority |
98 |
-> String |
99 |
-> (a -> m ()) |
100 |
-> GenericResult e a |
101 |
-> m () |
102 |
finishOrLog logPrio logPrefix = |
103 |
genericResult (logAt logPrio . (++) (logPrefix ++ ": ") . show) |
104 |
|
105 |
-- | Creates a stateless asynchronous task that handles errors in its actions. |
106 |
mkStatelessAsyncTask :: (MonadBaseControl IO m, MonadLog m, Show e) |
107 |
=> Priority |
108 |
-> String |
109 |
-> ResultT e m () |
110 |
-> m (AsyncWorker ()) |
111 |
mkStatelessAsyncTask logPrio logPrefix action = |
112 |
mkAsyncWorker $ runResultT action >>= finishOrLog logPrio logPrefix return |
113 |
|
114 |
-- | Creates an asynchronous task that handles errors in its actions. |
115 |
-- If an error occurs, it's logged and the internal state remains unchanged. |
116 |
mkStatefulAsyncTask :: (MonadBaseControl IO m, MonadLog m, Show e) |
117 |
=> Priority |
118 |
-> String |
119 |
-> s |
120 |
-> (s -> ResultT e m s) |
121 |
-> m (AsyncWorker ()) |
122 |
mkStatefulAsyncTask logPrio logPrefix start action = |
123 |
flip S.evalStateT start . mkAsyncWorker $ |
124 |
S.get >>= lift . runResultT . action |
125 |
>>= finishOrLog logPrio logPrefix S.put -- put on success |
126 |
|
127 |
-- | Construct an asynchronous worker whose action is to save the |
128 |
-- configuration to the master file. |
129 |
-- The worker's action reads the configuration using the given @IO@ action |
130 |
-- and uses 'FStat' to check if the configuration hasn't been modified by |
131 |
-- another process. |
132 |
saveConfigAsyncTask :: FilePath -- ^ Path to the config file |
133 |
-> FStat -- ^ The initial state of the config. file |
134 |
-> IO ConfigState -- ^ An action to read the current config |
135 |
-> ResultG (AsyncWorker ()) |
136 |
saveConfigAsyncTask fpath fstat cdRef = |
137 |
lift . mkStatefulAsyncTask |
138 |
EMERGENCY "Can't write the master configuration file" fstat |
139 |
$ \oldstat -> do |
140 |
cd <- liftBase (csConfigData `liftM` cdRef) |
141 |
writeConfigToFile cd fpath oldstat |
142 |
|
143 |
-- | Performs a RPC call on the given list of nodes and logs any failures. |
144 |
-- If any of the calls fails, fail the computation with 'failError'. |
145 |
execRpcCallAndLog :: (Rpc a b) => [Node] -> a -> ResultG () |
146 |
execRpcCallAndLog nodes req = do |
147 |
rs <- liftIO $ executeRpcCall nodes req |
148 |
es <- logRpcErrors rs |
149 |
unless (null es) $ failError "At least one of the RPC calls failed" |
150 |
|
151 |
-- | Construct an asynchronous worker whose action is to distribute the |
152 |
-- configuration to master candidates. |
153 |
distMCsAsyncTask :: RuntimeEnts |
154 |
-> FilePath -- ^ Path to the config file |
155 |
-> IO ConfigState -- ^ An action to read the current config |
156 |
-> ResultG (AsyncWorker ()) |
157 |
distMCsAsyncTask ents cpath cdRef = |
158 |
lift . mkStatelessAsyncTask ERROR "Can't distribute the configuration\ |
159 |
\ to master candidates" |
160 |
$ do |
161 |
cd <- liftBase (csConfigData <$> cdRef) :: ResultG ConfigData |
162 |
fupload <- prepareRpcCallUploadFile ents cpath |
163 |
execRpcCallAndLog (getMasterCandidates cd) fupload |