Merge branch 'stable-2.12' into stable-2.13
[ganeti-github.git] / src / Ganeti / Config.hs
1 {-| Implementation of the Ganeti configuration database.
2
3 -}
4
5 {-
6
7 Copyright (C) 2011, 2012 Google Inc.
8 All rights reserved.
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are
12 met:
13
14 1. Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
25 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 -}
34
35 module Ganeti.Config
36 ( LinkIpMap
37 , NdParamObject(..)
38 , loadConfig
39 , saveConfig
40 , getNodeInstances
41 , getNodeRole
42 , getNodeNdParams
43 , getDefaultNicLink
44 , getDefaultHypervisor
45 , getInstancesIpByLink
46 , getMasterNodes
47 , getMasterCandidates
48 , getMasterOrCandidates
49 , getMasterNetworkParameters
50 , getOnlineNodes
51 , getNode
52 , getInstance
53 , getDisk
54 , getFilterRule
55 , getGroup
56 , getGroupNdParams
57 , getGroupIpolicy
58 , getGroupDiskParams
59 , getGroupNodes
60 , getGroupInstances
61 , getGroupOfNode
62 , getInstPrimaryNode
63 , getInstMinorsForNode
64 , getInstAllNodes
65 , getInstDisks
66 , getInstDisksFromObj
67 , getDrbdMinorsForInstance
68 , getFilledInstHvParams
69 , getFilledInstBeParams
70 , getFilledInstOsParams
71 , getNetwork
72 , MAC
73 , getAllMACs
74 , getAllDrbdSecrets
75 , NodeLVsMap
76 , getInstanceLVsByNode
77 , getAllLVs
78 , buildLinkIpInstnameMap
79 , instNodes
80 ) where
81
82 import Control.Applicative
83 import Control.Monad
84 import Control.Monad.State
85 import qualified Data.Foldable as F
86 import Data.List (foldl', nub)
87 import Data.Monoid
88 import qualified Data.Map as M
89 import qualified Data.Set as S
90 import qualified Text.JSON as J
91 import System.IO
92
93 import Ganeti.BasicTypes
94 import qualified Ganeti.Constants as C
95 import Ganeti.Errors
96 import Ganeti.JSON
97 import Ganeti.Objects
98 import Ganeti.Types
99 import qualified Ganeti.Utils.MultiMap as MM
100
101 -- | Type alias for the link and ip map.
102 type LinkIpMap = M.Map String (M.Map String String)
103
104 -- * Operations on the whole configuration
105
106 -- | Reads the config file.
107 readConfig :: FilePath -> IO (Result String)
108 readConfig = runResultT . liftIO . readFile
109
110 -- | Parses the configuration file.
111 parseConfig :: String -> Result ConfigData
112 parseConfig = fromJResult "parsing configuration" . J.decodeStrict
113
114 -- | Encodes the configuration file.
115 encodeConfig :: ConfigData -> String
116 encodeConfig = J.encodeStrict
117
118 -- | Wrapper over 'readConfig' and 'parseConfig'.
119 loadConfig :: FilePath -> IO (Result ConfigData)
120 loadConfig = fmap (>>= parseConfig) . readConfig
121
122 -- | Wrapper over 'hPutStr' and 'encodeConfig'.
123 saveConfig :: Handle -> ConfigData -> IO ()
124 saveConfig fh = hPutStr fh . encodeConfig
125
126 -- * Query functions
127
128 -- | Computes the nodes covered by a disk.
129 computeDiskNodes :: Disk -> S.Set String
130 computeDiskNodes dsk =
131 case diskLogicalId dsk of
132 LIDDrbd8 nodeA nodeB _ _ _ _ -> S.fromList [nodeA, nodeB]
133 _ -> S.empty
134
135 -- | Computes all disk-related nodes of an instance. For non-DRBD,
136 -- this will be empty, for DRBD it will contain both the primary and
137 -- the secondaries.
138 instDiskNodes :: ConfigData -> Instance -> S.Set String
139 instDiskNodes cfg inst =
140 case getInstDisksFromObj cfg inst of
141 Ok disks -> S.unions $ map computeDiskNodes disks
142 Bad _ -> S.empty
143
144 -- | Computes all nodes of an instance.
145 instNodes :: ConfigData -> Instance -> S.Set String
146 instNodes cfg inst = instPrimaryNode inst `S.insert` instDiskNodes cfg inst
147
148 -- | Computes the secondary nodes of an instance. Since this is valid
149 -- only for DRBD, we call directly 'instDiskNodes', skipping over the
150 -- extra primary insert.
151 instSecondaryNodes :: ConfigData -> Instance -> S.Set String
152 instSecondaryNodes cfg inst =
153 instPrimaryNode inst `S.delete` instDiskNodes cfg inst
154
155 -- | Get instances of a given node.
156 -- The node is specified through its UUID.
157 getNodeInstances :: ConfigData -> String -> ([Instance], [Instance])
158 getNodeInstances cfg nname =
159 let all_inst = M.elems . fromContainer . configInstances $ cfg
160 pri_inst = filter ((== nname) . instPrimaryNode) all_inst
161 sec_inst = filter ((nname `S.member`) . instSecondaryNodes cfg) all_inst
162 in (pri_inst, sec_inst)
163
164 -- | Computes the role of a node.
165 getNodeRole :: ConfigData -> Node -> NodeRole
166 getNodeRole cfg node
167 | nodeUuid node == clusterMasterNode (configCluster cfg) = NRMaster
168 | nodeMasterCandidate node = NRCandidate
169 | nodeDrained node = NRDrained
170 | nodeOffline node = NROffline
171 | otherwise = NRRegular
172
173 -- | Get the list of the master nodes (usually one).
174 getMasterNodes :: ConfigData -> [Node]
175 getMasterNodes cfg =
176 filter ((==) NRMaster . getNodeRole cfg) . F.toList . configNodes $ cfg
177
178 -- | Get the list of master candidates, /not including/ the master itself.
179 getMasterCandidates :: ConfigData -> [Node]
180 getMasterCandidates cfg =
181 filter ((==) NRCandidate . getNodeRole cfg) . F.toList . configNodes $ cfg
182
183 -- | Get the list of master candidates, /including/ the master.
184 getMasterOrCandidates :: ConfigData -> [Node]
185 getMasterOrCandidates cfg =
186 let isMC r = (r == NRCandidate) || (r == NRMaster)
187 in filter (isMC . getNodeRole cfg) . F.toList . configNodes $ cfg
188
189 -- | Get the network parameters for the master IP address.
190 getMasterNetworkParameters :: ConfigData -> MasterNetworkParameters
191 getMasterNetworkParameters cfg =
192 let cluster = configCluster cfg
193 in MasterNetworkParameters
194 { masterNetworkParametersUuid = clusterMasterNode cluster
195 , masterNetworkParametersIp = clusterMasterIp cluster
196 , masterNetworkParametersNetmask = clusterMasterNetmask cluster
197 , masterNetworkParametersNetdev = clusterMasterNetdev cluster
198 , masterNetworkParametersIpFamily = clusterPrimaryIpFamily cluster
199 }
200
201 -- | Get the list of online nodes.
202 getOnlineNodes :: ConfigData -> [Node]
203 getOnlineNodes = filter (not . nodeOffline) . F.toList . configNodes
204
205 -- | Returns the default cluster link.
206 getDefaultNicLink :: ConfigData -> String
207 getDefaultNicLink =
208 nicpLink . (M.! C.ppDefault) . fromContainer .
209 clusterNicparams . configCluster
210
211 -- | Returns the default cluster hypervisor.
212 getDefaultHypervisor :: ConfigData -> Hypervisor
213 getDefaultHypervisor cfg =
214 case clusterEnabledHypervisors $ configCluster cfg of
215 -- FIXME: this case shouldn't happen (configuration broken), but
216 -- for now we handle it here because we're not authoritative for
217 -- the config
218 [] -> XenPvm
219 x:_ -> x
220
221 -- | Returns instances of a given link.
222 getInstancesIpByLink :: LinkIpMap -> String -> [String]
223 getInstancesIpByLink linkipmap link =
224 M.keys $ M.findWithDefault M.empty link linkipmap
225
226 -- | Generic lookup function that converts from a possible abbreviated
227 -- name to a full name.
228 getItem :: String -> String -> M.Map String a -> ErrorResult a
229 getItem kind name allitems = do
230 let lresult = lookupName (M.keys allitems) name
231 err msg = Bad $ OpPrereqError (kind ++ " name " ++ name ++ " " ++ msg)
232 ECodeNoEnt
233 fullname <- case lrMatchPriority lresult of
234 PartialMatch -> Ok $ lrContent lresult
235 ExactMatch -> Ok $ lrContent lresult
236 MultipleMatch -> err "has multiple matches"
237 FailMatch -> err "not found"
238 maybe (err "not found after successfull match?!") Ok $
239 M.lookup fullname allitems
240
241 -- | Looks up a node by name or uuid.
242 getNode :: ConfigData -> String -> ErrorResult Node
243 getNode cfg name =
244 let nodes = fromContainer (configNodes cfg)
245 in case getItem "Node" name nodes of
246 -- if not found by uuid, we need to look it up by name
247 Ok node -> Ok node
248 Bad _ -> let by_name = M.mapKeys
249 (nodeName . (M.!) nodes) nodes
250 in getItem "Node" name by_name
251
252 -- | Looks up an instance by name or uuid.
253 getInstance :: ConfigData -> String -> ErrorResult Instance
254 getInstance cfg name =
255 let instances = fromContainer (configInstances cfg)
256 in case getItem "Instance" name instances of
257 -- if not found by uuid, we need to look it up by name
258 Ok inst -> Ok inst
259 Bad _ -> let by_name = M.mapKeys
260 (instName . (M.!) instances) instances
261 in getItem "Instance" name by_name
262
263 -- | Looks up a disk by uuid.
264 getDisk :: ConfigData -> String -> ErrorResult Disk
265 getDisk cfg name =
266 let disks = fromContainer (configDisks cfg)
267 in getItem "Disk" name disks
268
269 -- | Looks up a filter by uuid.
270 getFilterRule :: ConfigData -> String -> ErrorResult FilterRule
271 getFilterRule cfg name =
272 let filters = fromContainer (configFilters cfg)
273 in getItem "Filter" name filters
274
275 -- | Looks up a node group by name or uuid.
276 getGroup :: ConfigData -> String -> ErrorResult NodeGroup
277 getGroup cfg name =
278 let groups = fromContainer (configNodegroups cfg)
279 in case getItem "NodeGroup" name groups of
280 -- if not found by uuid, we need to look it up by name, slow
281 Ok grp -> Ok grp
282 Bad _ -> let by_name = M.mapKeys
283 (groupName . (M.!) groups) groups
284 in getItem "NodeGroup" name by_name
285
286 -- | Computes a node group's node params.
287 getGroupNdParams :: ConfigData -> NodeGroup -> FilledNDParams
288 getGroupNdParams cfg ng =
289 fillNDParams (clusterNdparams $ configCluster cfg) (groupNdparams ng)
290
291 -- | Computes a node group's ipolicy.
292 getGroupIpolicy :: ConfigData -> NodeGroup -> FilledIPolicy
293 getGroupIpolicy cfg ng =
294 fillIPolicy (clusterIpolicy $ configCluster cfg) (groupIpolicy ng)
295
296 -- | Computes a group\'s (merged) disk params.
297 getGroupDiskParams :: ConfigData -> NodeGroup -> GroupDiskParams
298 getGroupDiskParams cfg ng =
299 GenericContainer $
300 fillDict (fromContainer . clusterDiskparams $ configCluster cfg)
301 (fromContainer $ groupDiskparams ng) []
302
303 -- | Get nodes of a given node group.
304 getGroupNodes :: ConfigData -> String -> [Node]
305 getGroupNodes cfg gname =
306 let all_nodes = M.elems . fromContainer . configNodes $ cfg in
307 filter ((==gname) . nodeGroup) all_nodes
308
309 -- | Get (primary, secondary) instances of a given node group.
310 getGroupInstances :: ConfigData -> String -> ([Instance], [Instance])
311 getGroupInstances cfg gname =
312 let gnodes = map nodeUuid (getGroupNodes cfg gname)
313 ginsts = map (getNodeInstances cfg) gnodes in
314 (concatMap fst ginsts, concatMap snd ginsts)
315
316 -- | Retrieves the instance hypervisor params, missing values filled with
317 -- cluster defaults.
318 getFilledInstHvParams :: [String] -> ConfigData -> Instance -> HvParams
319 getFilledInstHvParams globals cfg inst =
320 -- First get the defaults of the parent
321 let hvName = instHypervisor inst
322 hvParamMap = fromContainer . clusterHvparams $ configCluster cfg
323 parentHvParams = maybe M.empty fromContainer $ M.lookup hvName hvParamMap
324 -- Then the os defaults for the given hypervisor
325 osName = instOs inst
326 osParamMap = fromContainer . clusterOsHvp $ configCluster cfg
327 osHvParamMap = maybe M.empty fromContainer $ M.lookup osName osParamMap
328 osHvParams = maybe M.empty fromContainer $ M.lookup hvName osHvParamMap
329 -- Then the child
330 childHvParams = fromContainer . instHvparams $ inst
331 -- Helper function
332 fillFn con val = fillDict con val globals
333 in GenericContainer $ fillFn (fillFn parentHvParams osHvParams) childHvParams
334
335 -- | Retrieves the instance backend params, missing values filled with cluster
336 -- defaults.
337 getFilledInstBeParams :: ConfigData -> Instance -> ErrorResult FilledBeParams
338 getFilledInstBeParams cfg inst = do
339 let beParamMap = fromContainer . clusterBeparams . configCluster $ cfg
340 parentParams <- getItem "FilledBeParams" C.ppDefault beParamMap
341 return $ fillBeParams parentParams (instBeparams inst)
342
343 -- | Retrieves the instance os params, missing values filled with cluster
344 -- defaults. This does NOT include private and secret parameters.
345 getFilledInstOsParams :: ConfigData -> Instance -> OsParams
346 getFilledInstOsParams cfg inst =
347 let osLookupName = takeWhile (/= '+') (instOs inst)
348 osParamMap = fromContainer . clusterOsparams $ configCluster cfg
349 childOsParams = instOsparams inst
350 in case getItem "OsParams" osLookupName osParamMap of
351 Ok parentOsParams -> GenericContainer $
352 fillDict (fromContainer parentOsParams)
353 (fromContainer childOsParams) []
354 Bad _ -> childOsParams
355
356 -- | Looks up an instance's primary node.
357 getInstPrimaryNode :: ConfigData -> String -> ErrorResult Node
358 getInstPrimaryNode cfg name =
359 liftM instPrimaryNode (getInstance cfg name) >>= getNode cfg
360
361 -- | Retrieves all nodes hosting a DRBD disk
362 getDrbdDiskNodes :: ConfigData -> Disk -> [Node]
363 getDrbdDiskNodes cfg disk =
364 let retrieved = case diskLogicalId disk of
365 LIDDrbd8 nodeA nodeB _ _ _ _ ->
366 justOk [getNode cfg nodeA, getNode cfg nodeB]
367 _ -> []
368 in retrieved ++ concatMap (getDrbdDiskNodes cfg) (diskChildren disk)
369
370 -- | Retrieves all the nodes of the instance.
371 --
372 -- As instances not using DRBD can be sent as a parameter as well,
373 -- the primary node has to be appended to the results.
374 getInstAllNodes :: ConfigData -> String -> ErrorResult [Node]
375 getInstAllNodes cfg name = do
376 inst_disks <- getInstDisks cfg name
377 let diskNodes = concatMap (getDrbdDiskNodes cfg) inst_disks
378 pNode <- getInstPrimaryNode cfg name
379 return . nub $ pNode:diskNodes
380
381 -- | Get disks for a given instance.
382 -- The instance is specified by name or uuid.
383 getInstDisks :: ConfigData -> String -> ErrorResult [Disk]
384 getInstDisks cfg iname =
385 getInstance cfg iname >>= mapM (getDisk cfg) . instDisks
386
387 -- | Get disks for a given instance object.
388 getInstDisksFromObj :: ConfigData -> Instance -> ErrorResult [Disk]
389 getInstDisksFromObj cfg =
390 getInstDisks cfg . instUuid
391
392 -- | Collects a value for all DRBD disks
393 collectFromDrbdDisks
394 :: (Monoid a)
395 => (String -> String -> Int -> Int -> Int -> Private DRBDSecret -> a)
396 -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
397 -> Disk -> a
398 collectFromDrbdDisks f = col
399 where
400 col Disk { diskLogicalId = (LIDDrbd8 nA nB port mA mB secret)
401 , diskChildren = ch
402 } = f nA nB port mA mB secret <> F.foldMap col ch
403 col d = F.foldMap col (diskChildren d)
404
405 -- | Returns the DRBD secrets of a given 'Disk'
406 getDrbdSecretsForDisk :: Disk -> [DRBDSecret]
407 getDrbdSecretsForDisk = collectFromDrbdDisks
408 (\_ _ _ _ _ (Private secret) -> [secret])
409
410 -- | Returns the DRBD minors of a given 'Disk'
411 getDrbdMinorsForDisk :: Disk -> [(Int, String)]
412 getDrbdMinorsForDisk =
413 collectFromDrbdDisks (\nA nB _ mnA mnB _ -> [(mnA, nA), (mnB, nB)])
414
415 -- | Filters DRBD minors for a given node.
416 getDrbdMinorsForNode :: String -> Disk -> [(Int, String)]
417 getDrbdMinorsForNode node disk =
418 let child_minors = concatMap (getDrbdMinorsForNode node) (diskChildren disk)
419 this_minors =
420 case diskLogicalId disk of
421 LIDDrbd8 nodeA nodeB _ minorA minorB _
422 | nodeA == node -> [(minorA, nodeB)]
423 | nodeB == node -> [(minorB, nodeA)]
424 _ -> []
425 in this_minors ++ child_minors
426
427 -- | Returns the DRBD minors of a given instance
428 getDrbdMinorsForInstance :: ConfigData -> Instance
429 -> ErrorResult [(Int, String)]
430 getDrbdMinorsForInstance cfg =
431 liftM (concatMap getDrbdMinorsForDisk) . getInstDisksFromObj cfg
432
433 -- | String for primary role.
434 rolePrimary :: String
435 rolePrimary = "primary"
436
437 -- | String for secondary role.
438 roleSecondary :: String
439 roleSecondary = "secondary"
440
441 -- | Gets the list of DRBD minors for an instance that are related to
442 -- a given node.
443 getInstMinorsForNode :: ConfigData
444 -> String -- ^ The UUID of a node.
445 -> Instance
446 -> [(String, Int, String, String, String, String)]
447 getInstMinorsForNode cfg node inst =
448 let role = if node == instPrimaryNode inst
449 then rolePrimary
450 else roleSecondary
451 iname = instName inst
452 inst_disks = case getInstDisksFromObj cfg inst of
453 Ok disks -> disks
454 Bad _ -> []
455 -- FIXME: the disk/ build there is hack-ish; unify this in a
456 -- separate place, or reuse the iv_name (but that is deprecated on
457 -- the Python side)
458 in concatMap (\(idx, dsk) ->
459 [(node, minor, iname, "disk/" ++ show idx, role, peer)
460 | (minor, peer) <- getDrbdMinorsForNode node dsk]) .
461 zip [(0::Int)..] $ inst_disks
462
463 -- | Builds link -> ip -> instname map.
464 --
465 -- TODO: improve this by splitting it into multiple independent functions:
466 --
467 -- * abstract the \"fetch instance with filled params\" functionality
468 --
469 -- * abstsract the [instance] -> [(nic, instance_name)] part
470 --
471 -- * etc.
472 buildLinkIpInstnameMap :: ConfigData -> LinkIpMap
473 buildLinkIpInstnameMap cfg =
474 let cluster = configCluster cfg
475 instances = M.elems . fromContainer . configInstances $ cfg
476 defparams = (M.!) (fromContainer $ clusterNicparams cluster) C.ppDefault
477 nics = concatMap (\i -> [(instName i, nic) | nic <- instNics i])
478 instances
479 in foldl' (\accum (iname, nic) ->
480 let pparams = nicNicparams nic
481 fparams = fillNicParams defparams pparams
482 link = nicpLink fparams
483 in case nicIp nic of
484 Nothing -> accum
485 Just ip -> let oldipmap = M.findWithDefault M.empty
486 link accum
487 newipmap = M.insert ip iname oldipmap
488 in M.insert link newipmap accum
489 ) M.empty nics
490
491
492 -- | Returns a node's group, with optional failure if we can't find it
493 -- (configuration corrupt).
494 getGroupOfNode :: ConfigData -> Node -> Maybe NodeGroup
495 getGroupOfNode cfg node =
496 M.lookup (nodeGroup node) (fromContainer . configNodegroups $ cfg)
497
498 -- | Returns a node's ndparams, filled.
499 getNodeNdParams :: ConfigData -> Node -> Maybe FilledNDParams
500 getNodeNdParams cfg node = do
501 group <- getGroupOfNode cfg node
502 let gparams = getGroupNdParams cfg group
503 return $ fillNDParams gparams (nodeNdparams node)
504
505 -- * Network
506
507 -- | Looks up a network. If looking up by uuid fails, we look up
508 -- by name.
509 getNetwork :: ConfigData -> String -> ErrorResult Network
510 getNetwork cfg name =
511 let networks = fromContainer (configNetworks cfg)
512 in case getItem "Network" name networks of
513 Ok net -> Ok net
514 Bad _ -> let by_name = M.mapKeys
515 (fromNonEmpty . networkName . (M.!) networks)
516 networks
517 in getItem "Network" name by_name
518
519 -- ** MACs
520
521 type MAC = String
522
523 -- | Returns all MAC addresses used in the cluster.
524 getAllMACs :: ConfigData -> [MAC]
525 getAllMACs = F.foldMap (map nicMac . instNics) . configInstances
526
527 -- ** DRBD secrets
528
529 getAllDrbdSecrets :: ConfigData -> [DRBDSecret]
530 getAllDrbdSecrets = F.foldMap getDrbdSecretsForDisk . configDisks
531
532 -- ** LVs
533
534 -- | A map from node UUIDs to
535 --
536 -- FIXME: After adding designated types for UUIDs,
537 -- use them to replace 'String' here.
538 type NodeLVsMap = MM.MultiMap String LogicalVolume
539
540 getInstanceLVsByNode :: ConfigData -> Instance -> ErrorResult NodeLVsMap
541 getInstanceLVsByNode cd inst =
542 (MM.fromList . lvsByNode (instPrimaryNode inst))
543 <$> getInstDisksFromObj cd inst
544 where
545 lvsByNode :: String -> [Disk] -> [(String, LogicalVolume)]
546 lvsByNode node = concatMap (lvsByNode1 node)
547 lvsByNode1 :: String -> Disk -> [(String, LogicalVolume)]
548 lvsByNode1 _ Disk { diskLogicalId = (LIDDrbd8 nA nB _ _ _ _)
549 , diskChildren = ch
550 } = lvsByNode nA ch ++ lvsByNode nB ch
551 lvsByNode1 node Disk { diskLogicalId = (LIDPlain lv) }
552 = [(node, lv)]
553 lvsByNode1 node Disk { diskChildren = ch }
554 = lvsByNode node ch
555
556 getAllLVs :: ConfigData -> ErrorResult (S.Set LogicalVolume)
557 getAllLVs cd = mconcat <$> mapM (liftM MM.values . getInstanceLVsByNode cd)
558 (F.toList $ configInstances cd)
559
560 -- * ND params
561
562 -- | Type class denoting objects which have node parameters.
563 class NdParamObject a where
564 getNdParamsOf :: ConfigData -> a -> Maybe FilledNDParams
565
566 instance NdParamObject Node where
567 getNdParamsOf = getNodeNdParams
568
569 instance NdParamObject NodeGroup where
570 getNdParamsOf cfg = Just . getGroupNdParams cfg
571
572 instance NdParamObject Cluster where
573 getNdParamsOf _ = Just . clusterNdparams