Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Config.hs @ a1f35d0a

History | View | Annotate | Download (15.3 kB)

1
{-| Implementation of the Ganeti configuration database.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2011, 2012 Google Inc.
8

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

    
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18

    
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Ganeti.Config
27
    ( LinkIpMap
28
    , NdParamObject(..)
29
    , loadConfig
30
    , saveConfig
31
    , getNodeInstances
32
    , getNodeRole
33
    , getNodeNdParams
34
    , getDefaultNicLink
35
    , getDefaultHypervisor
36
    , getInstancesIpByLink
37
    , getMasterCandidates
38
    , getOnlineNodes
39
    , getNode
40
    , getInstance
41
    , getGroup
42
    , getGroupNdParams
43
    , getGroupIpolicy
44
    , getGroupDiskParams
45
    , getGroupNodes
46
    , getGroupInstances
47
    , getGroupOfNode
48
    , getInstPrimaryNode
49
    , getInstMinorsForNode
50
    , getInstAllNodes
51
    , getFilledInstHvParams
52
    , getFilledInstBeParams
53
    , getFilledInstOsParams
54
    , getNetwork
55
    , buildLinkIpInstnameMap
56
    , instNodes
57
    ) where
58

    
59
import Control.Monad (liftM)
60
import qualified Data.Foldable as F
61
import Data.List (foldl', nub)
62
import qualified Data.Map as M
63
import qualified Data.Set as S
64
import qualified Text.JSON as J
65
import System.IO
66

    
67
import Ganeti.BasicTypes
68
import qualified Ganeti.Constants as C
69
import Ganeti.Errors
70
import Ganeti.JSON
71
import Ganeti.Objects
72
import Ganeti.Types
73

    
74
-- | Type alias for the link and ip map.
75
type LinkIpMap = M.Map String (M.Map String String)
76

    
77
-- | Type class denoting objects which have node parameters.
78
class NdParamObject a where
79
  getNdParamsOf :: ConfigData -> a -> Maybe FilledNDParams
80

    
81
-- | Reads the config file.
82
readConfig :: FilePath -> IO (Result String)
83
readConfig = runResultT . liftIO . readFile
84

    
85
-- | Parses the configuration file.
86
parseConfig :: String -> Result ConfigData
87
parseConfig = fromJResult "parsing configuration" . J.decodeStrict
88

    
89
-- | Encodes the configuration file.
90
encodeConfig :: ConfigData -> String
91
encodeConfig = J.encodeStrict
92

    
93
-- | Wrapper over 'readConfig' and 'parseConfig'.
94
loadConfig :: FilePath -> IO (Result ConfigData)
95
loadConfig = fmap (>>= parseConfig) . readConfig
96

    
97
-- | Wrapper over 'hPutStr' and 'encodeConfig'.
98
saveConfig :: Handle -> ConfigData -> IO ()
99
saveConfig fh = hPutStr fh . encodeConfig
100

    
101
-- * Query functions
102

    
103
-- | Computes the nodes covered by a disk.
104
computeDiskNodes :: Disk -> S.Set String
105
computeDiskNodes dsk =
106
  case diskLogicalId dsk of
107
    LIDDrbd8 nodeA nodeB _ _ _ _ -> S.fromList [nodeA, nodeB]
108
    _ -> S.empty
109

    
110
-- | Computes all disk-related nodes of an instance. For non-DRBD,
111
-- this will be empty, for DRBD it will contain both the primary and
112
-- the secondaries.
113
instDiskNodes :: Instance -> S.Set String
114
instDiskNodes = S.unions . map computeDiskNodes . instDisks
115

    
116
-- | Computes all nodes of an instance.
117
instNodes :: Instance -> S.Set String
118
instNodes inst = instPrimaryNode inst `S.insert` instDiskNodes inst
119

    
120
-- | Computes the secondary nodes of an instance. Since this is valid
121
-- only for DRBD, we call directly 'instDiskNodes', skipping over the
122
-- extra primary insert.
123
instSecondaryNodes :: Instance -> S.Set String
124
instSecondaryNodes inst =
125
  instPrimaryNode inst `S.delete` instDiskNodes inst
126

    
127
-- | Get instances of a given node.
128
-- The node is specified through its UUID.
129
getNodeInstances :: ConfigData -> String -> ([Instance], [Instance])
130
getNodeInstances cfg nname =
131
    let all_inst = M.elems . fromContainer . configInstances $ cfg
132
        pri_inst = filter ((== nname) . instPrimaryNode) all_inst
133
        sec_inst = filter ((nname `S.member`) . instSecondaryNodes) all_inst
134
    in (pri_inst, sec_inst)
135

    
136
-- | Computes the role of a node.
137
getNodeRole :: ConfigData -> Node -> NodeRole
138
getNodeRole cfg node
139
  | nodeUuid node == clusterMasterNode (configCluster cfg) = NRMaster
140
  | nodeMasterCandidate node = NRCandidate
141
  | nodeDrained node = NRDrained
142
  | nodeOffline node = NROffline
143
  | otherwise = NRRegular
144

    
145
-- | Get the list of master candidates.
146
getMasterCandidates :: ConfigData -> [Node]
147
getMasterCandidates cfg = 
148
  filter ((==) NRCandidate . getNodeRole cfg) . F.toList . configNodes $ cfg
149

    
150
-- | Get the list of online nodes.
151
getOnlineNodes :: ConfigData -> [Node]
152
getOnlineNodes = filter (not . nodeOffline) . F.toList . configNodes
153

    
154
-- | Returns the default cluster link.
155
getDefaultNicLink :: ConfigData -> String
156
getDefaultNicLink =
157
  nicpLink . (M.! C.ppDefault) . fromContainer .
158
  clusterNicparams . configCluster
159

    
160
-- | Returns the default cluster hypervisor.
161
getDefaultHypervisor :: ConfigData -> Hypervisor
162
getDefaultHypervisor cfg =
163
  case clusterEnabledHypervisors $ configCluster cfg of
164
    -- FIXME: this case shouldn't happen (configuration broken), but
165
    -- for now we handle it here because we're not authoritative for
166
    -- the config
167
    []  -> XenPvm
168
    x:_ -> x
169

    
170
-- | Returns instances of a given link.
171
getInstancesIpByLink :: LinkIpMap -> String -> [String]
172
getInstancesIpByLink linkipmap link =
173
  M.keys $ M.findWithDefault M.empty link linkipmap
174

    
175
-- | Generic lookup function that converts from a possible abbreviated
176
-- name to a full name.
177
getItem :: String -> String -> M.Map String a -> ErrorResult a
178
getItem kind name allitems = do
179
  let lresult = lookupName (M.keys allitems) name
180
      err msg = Bad $ OpPrereqError (kind ++ " name " ++ name ++ " " ++ msg)
181
                        ECodeNoEnt
182
  fullname <- case lrMatchPriority lresult of
183
                PartialMatch -> Ok $ lrContent lresult
184
                ExactMatch -> Ok $ lrContent lresult
185
                MultipleMatch -> err "has multiple matches"
186
                FailMatch -> err "not found"
187
  maybe (err "not found after successfull match?!") Ok $
188
        M.lookup fullname allitems
189

    
190
-- | Looks up a node by name or uuid.
191
getNode :: ConfigData -> String -> ErrorResult Node
192
getNode cfg name =
193
  let nodes = fromContainer (configNodes cfg)
194
  in case getItem "Node" name nodes of
195
       -- if not found by uuid, we need to look it up by name
196
       Ok node -> Ok node
197
       Bad _ -> let by_name = M.mapKeys
198
                              (nodeName . (M.!) nodes) nodes
199
                in getItem "Node" name by_name
200

    
201
-- | Looks up an instance by name or uuid.
202
getInstance :: ConfigData -> String -> ErrorResult Instance
203
getInstance cfg name =
204
  let instances = fromContainer (configInstances cfg)
205
  in case getItem "Instance" name instances of
206
       -- if not found by uuid, we need to look it up by name
207
       Ok inst -> Ok inst
208
       Bad _ -> let by_name = M.mapKeys
209
                              (instName . (M.!) instances) instances
210
                in getItem "Instance" name by_name
211

    
212
-- | Looks up a node group by name or uuid.
213
getGroup :: ConfigData -> String -> ErrorResult NodeGroup
214
getGroup cfg name =
215
  let groups = fromContainer (configNodegroups cfg)
216
  in case getItem "NodeGroup" name groups of
217
       -- if not found by uuid, we need to look it up by name, slow
218
       Ok grp -> Ok grp
219
       Bad _ -> let by_name = M.mapKeys
220
                              (groupName . (M.!) groups) groups
221
                in getItem "NodeGroup" name by_name
222

    
223
-- | Computes a node group's node params.
224
getGroupNdParams :: ConfigData -> NodeGroup -> FilledNDParams
225
getGroupNdParams cfg ng =
226
  fillNDParams (clusterNdparams $ configCluster cfg) (groupNdparams ng)
227

    
228
-- | Computes a node group's ipolicy.
229
getGroupIpolicy :: ConfigData -> NodeGroup -> FilledIPolicy
230
getGroupIpolicy cfg ng =
231
  fillIPolicy (clusterIpolicy $ configCluster cfg) (groupIpolicy ng)
232

    
233
-- | Computes a group\'s (merged) disk params.
234
getGroupDiskParams :: ConfigData -> NodeGroup -> DiskParams
235
getGroupDiskParams cfg ng =
236
  GenericContainer $
237
  fillDict (fromContainer . clusterDiskparams $ configCluster cfg)
238
           (fromContainer $ groupDiskparams ng) []
239

    
240
-- | Get nodes of a given node group.
241
getGroupNodes :: ConfigData -> String -> [Node]
242
getGroupNodes cfg gname =
243
  let all_nodes = M.elems . fromContainer . configNodes $ cfg in
244
  filter ((==gname) . nodeGroup) all_nodes
245

    
246
-- | Get (primary, secondary) instances of a given node group.
247
getGroupInstances :: ConfigData -> String -> ([Instance], [Instance])
248
getGroupInstances cfg gname =
249
  let gnodes = map nodeUuid (getGroupNodes cfg gname)
250
      ginsts = map (getNodeInstances cfg) gnodes in
251
  (concatMap fst ginsts, concatMap snd ginsts)
252

    
253
-- | Looks up a network. If looking up by uuid fails, we look up
254
-- by name.
255
getNetwork :: ConfigData -> String -> ErrorResult Network
256
getNetwork cfg name =
257
  let networks = fromContainer (configNetworks cfg)
258
  in case getItem "Network" name networks of
259
       Ok net -> Ok net
260
       Bad _ -> let by_name = M.mapKeys
261
                              (fromNonEmpty . networkName . (M.!) networks)
262
                              networks
263
                in getItem "Network" name by_name
264

    
265
-- | Retrieves the instance hypervisor params, missing values filled with
266
-- cluster defaults.
267
getFilledInstHvParams :: [String] -> ConfigData -> Instance -> HvParams
268
getFilledInstHvParams globals cfg inst =
269
  -- First get the defaults of the parent
270
  let hvName = hypervisorToRaw . instHypervisor $ inst
271
      hvParamMap = fromContainer . clusterHvparams $ configCluster cfg
272
      parentHvParams = maybe M.empty fromContainer $ M.lookup hvName hvParamMap
273
  -- Then the os defaults for the given hypervisor
274
      osName = instOs inst
275
      osParamMap = fromContainer . clusterOsHvp $ configCluster cfg
276
      osHvParamMap = maybe M.empty fromContainer $ M.lookup osName osParamMap
277
      osHvParams = maybe M.empty fromContainer $ M.lookup hvName osHvParamMap
278
  -- Then the child
279
      childHvParams = fromContainer . instHvparams $ inst
280
  -- Helper function
281
      fillFn con val = fillDict con val globals
282
  in GenericContainer $ fillFn (fillFn parentHvParams osHvParams) childHvParams
283

    
284
-- | Retrieves the instance backend params, missing values filled with cluster
285
-- defaults.
286
getFilledInstBeParams :: ConfigData -> Instance -> ErrorResult FilledBeParams
287
getFilledInstBeParams cfg inst = do
288
  let beParamMap = fromContainer . clusterBeparams . configCluster $ cfg
289
  parentParams <- getItem "FilledBeParams" C.ppDefault beParamMap
290
  return $ fillBeParams parentParams (instBeparams inst)
291

    
292
-- | Retrieves the instance os params, missing values filled with cluster
293
-- defaults. This does NOT include private and secret parameters.
294
getFilledInstOsParams :: ConfigData -> Instance -> OsParams
295
getFilledInstOsParams cfg inst =
296
  let osLookupName = takeWhile (/= '+') (instOs inst)
297
      osParamMap = fromContainer . clusterOsparams $ configCluster cfg
298
      childOsParams = instOsparams inst
299
  in case getItem "OsParams" osLookupName osParamMap of
300
       Ok parentOsParams -> GenericContainer $
301
                              fillDict (fromContainer parentOsParams)
302
                                       (fromContainer childOsParams) []
303
       Bad _             -> childOsParams
304

    
305
-- | Looks up an instance's primary node.
306
getInstPrimaryNode :: ConfigData -> String -> ErrorResult Node
307
getInstPrimaryNode cfg name =
308
  liftM instPrimaryNode (getInstance cfg name) >>= getNode cfg
309

    
310
-- | Retrieves all nodes hosting a DRBD disk
311
getDrbdDiskNodes :: ConfigData -> Disk -> [Node]
312
getDrbdDiskNodes cfg disk =
313
  let retrieved = case diskLogicalId disk of
314
                    LIDDrbd8 nodeA nodeB _ _ _ _ ->
315
                      justOk [getNode cfg nodeA, getNode cfg nodeB]
316
                    _                            -> []
317
  in retrieved ++ concatMap (getDrbdDiskNodes cfg) (diskChildren disk)
318

    
319
-- | Retrieves all the nodes of the instance.
320
--
321
-- As instances not using DRBD can be sent as a parameter as well,
322
-- the primary node has to be appended to the results.
323
getInstAllNodes :: ConfigData -> String -> ErrorResult [Node]
324
getInstAllNodes cfg name = do
325
  inst <- getInstance cfg name
326
  let diskNodes = concatMap (getDrbdDiskNodes cfg) $ instDisks inst
327
  pNode <- getInstPrimaryNode cfg name
328
  return . nub $ pNode:diskNodes
329

    
330
-- | Filters DRBD minors for a given node.
331
getDrbdMinorsForNode :: String -> Disk -> [(Int, String)]
332
getDrbdMinorsForNode node disk =
333
  let child_minors = concatMap (getDrbdMinorsForNode node) (diskChildren disk)
334
      this_minors =
335
        case diskLogicalId disk of
336
          LIDDrbd8 nodeA nodeB _ minorA minorB _
337
            | nodeA == node -> [(minorA, nodeB)]
338
            | nodeB == node -> [(minorB, nodeA)]
339
          _ -> []
340
  in this_minors ++ child_minors
341

    
342
-- | String for primary role.
343
rolePrimary :: String
344
rolePrimary = "primary"
345

    
346
-- | String for secondary role.
347
roleSecondary :: String
348
roleSecondary = "secondary"
349

    
350
-- | Gets the list of DRBD minors for an instance that are related to
351
-- a given node.
352
getInstMinorsForNode :: String -> Instance
353
                     -> [(String, Int, String, String, String, String)]
354
getInstMinorsForNode node inst =
355
  let role = if node == instPrimaryNode inst
356
               then rolePrimary
357
               else roleSecondary
358
      iname = instName inst
359
  -- FIXME: the disk/ build there is hack-ish; unify this in a
360
  -- separate place, or reuse the iv_name (but that is deprecated on
361
  -- the Python side)
362
  in concatMap (\(idx, dsk) ->
363
            [(node, minor, iname, "disk/" ++ show idx, role, peer)
364
               | (minor, peer) <- getDrbdMinorsForNode node dsk]) .
365
     zip [(0::Int)..] . instDisks $ inst
366

    
367
-- | Builds link -> ip -> instname map.
368
--
369
-- TODO: improve this by splitting it into multiple independent functions:
370
--
371
-- * abstract the \"fetch instance with filled params\" functionality
372
--
373
-- * abstsract the [instance] -> [(nic, instance_name)] part
374
--
375
-- * etc.
376
buildLinkIpInstnameMap :: ConfigData -> LinkIpMap
377
buildLinkIpInstnameMap cfg =
378
  let cluster = configCluster cfg
379
      instances = M.elems . fromContainer . configInstances $ cfg
380
      defparams = (M.!) (fromContainer $ clusterNicparams cluster) C.ppDefault
381
      nics = concatMap (\i -> [(instName i, nic) | nic <- instNics i])
382
             instances
383
  in foldl' (\accum (iname, nic) ->
384
               let pparams = nicNicparams nic
385
                   fparams = fillNicParams defparams pparams
386
                   link = nicpLink fparams
387
               in case nicIp nic of
388
                    Nothing -> accum
389
                    Just ip -> let oldipmap = M.findWithDefault M.empty
390
                                              link accum
391
                                   newipmap = M.insert ip iname oldipmap
392
                               in M.insert link newipmap accum
393
            ) M.empty nics
394

    
395

    
396
-- | Returns a node's group, with optional failure if we can't find it
397
-- (configuration corrupt).
398
getGroupOfNode :: ConfigData -> Node -> Maybe NodeGroup
399
getGroupOfNode cfg node =
400
  M.lookup (nodeGroup node) (fromContainer . configNodegroups $ cfg)
401

    
402
-- | Returns a node's ndparams, filled.
403
getNodeNdParams :: ConfigData -> Node -> Maybe FilledNDParams
404
getNodeNdParams cfg node = do
405
  group <- getGroupOfNode cfg node
406
  let gparams = getGroupNdParams cfg group
407
  return $ fillNDParams gparams (nodeNdparams node)
408

    
409
instance NdParamObject Node where
410
  getNdParamsOf = getNodeNdParams
411

    
412
instance NdParamObject NodeGroup where
413
  getNdParamsOf cfg = Just . getGroupNdParams cfg
414

    
415
instance NdParamObject Cluster where
416
  getNdParamsOf _ = Just . clusterNdparams