blob: 5687b54322fcba839cc43bb830e4d0e87bd8dc45 [file] [log] [blame]
{-# LANGUAGE ViewPatterns #-}
{-| Implementation of the Ganeti configuration database.
-}
{-
Copyright (C) 2011, 2012 Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-}
module Ganeti.Config
( LinkIpMap
, NdParamObject(..)
, loadConfig
, saveConfig
, getNodeInstances
, getNodeRole
, getNodeNdParams
, getDefaultNicLink
, getDefaultHypervisor
, getInstancesIpByLink
, getMasterNodes
, getMasterCandidates
, getMasterOrCandidates
, getMasterNetworkParameters
, getOnlineNodes
, getNode
, getInstance
, getDisk
, getFilterRule
, getGroup
, getGroupNdParams
, getGroupIpolicy
, getGroupDiskParams
, getGroupNodes
, getGroupInstances
, getGroupOfNode
, getInstPrimaryNode
, getInstMinorsForNode
, getInstAllNodes
, getInstDisks
, getInstDisksFromObj
, getDrbdMinorsForDisk
, getDrbdMinorsForInstance
, getFilledHvStateParams
, getFilledInstHvParams
, getFilledInstBeParams
, getFilledInstOsParams
, getNetwork
, MAC
, getAllMACs
, getAllDrbdSecrets
, NodeLVsMap
, getInstanceLVsByNode
, getAllLVs
, buildLinkIpInstnameMap
, instNodes
) where
import Prelude ()
import Ganeti.Prelude
import Control.Arrow ((&&&))
import Control.Monad (liftM)
import qualified Data.ByteString as BS
import qualified Data.ByteString.UTF8 as UTF8
import qualified Data.Foldable as F
import Data.List (foldl', nub)
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Monoid
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Text.JSON as J
import System.IO
import Ganeti.BasicTypes
import qualified Ganeti.Constants as C
import qualified Ganeti.ConstantUtils as CU
import Ganeti.Errors
import Ganeti.JSON (fromJResult, fromContainer, GenericContainer(..))
import Ganeti.Objects
import Ganeti.Types
import qualified Ganeti.Utils.MultiMap as MM
-- | Type alias for the link and ip map.
type LinkIpMap = M.Map String (M.Map String String)
-- * Operations on the whole configuration
-- | Reads the config file.
readConfig :: FilePath -> IO (Result String)
readConfig = runResultT . liftIO . readFile
-- | Parses the configuration file.
parseConfig :: String -> Result ConfigData
parseConfig = fromJResult "parsing configuration" . J.decodeStrict
-- | Encodes the configuration file.
encodeConfig :: ConfigData -> String
encodeConfig = J.encodeStrict
-- | Wrapper over 'readConfig' and 'parseConfig'.
loadConfig :: FilePath -> IO (Result ConfigData)
loadConfig = fmap (>>= parseConfig) . readConfig
-- | Wrapper over 'hPutStr' and 'encodeConfig'.
saveConfig :: Handle -> ConfigData -> IO ()
saveConfig fh = hPutStr fh . encodeConfig
-- * Query functions
-- | Annotate Nothing as missing parameter and apply the given
-- transformation otherwise
withMissingParam :: String -> (a -> ErrorResult b) -> Maybe a -> ErrorResult b
withMissingParam = maybe . Bad . ParameterError
-- | Computes the nodes covered by a disk.
computeDiskNodes :: Disk -> S.Set String
computeDiskNodes dsk =
case diskLogicalId dsk of
Just (LIDDrbd8 nodeA nodeB _ _ _ _) -> S.fromList [nodeA, nodeB]
_ -> S.empty
-- | Computes all disk-related nodes of an instance. For non-DRBD,
-- this will be empty, for DRBD it will contain both the primary and
-- the secondaries.
instDiskNodes :: ConfigData -> Instance -> S.Set String
instDiskNodes cfg inst =
case getInstDisksFromObj cfg inst of
Ok disks -> S.unions $ map computeDiskNodes disks
Bad _ -> S.empty
-- | Computes all nodes of an instance.
instNodes :: ConfigData -> Instance -> S.Set String
instNodes cfg inst = maybe id S.insert (instPrimaryNode inst)
$ instDiskNodes cfg inst
-- | Computes the secondary node UUID for a DRBD disk
computeDiskSecondaryNode :: Disk -> String -> Maybe String
computeDiskSecondaryNode dsk primary =
case diskLogicalId dsk of
Just (LIDDrbd8 a b _ _ _ _) -> Just $ if primary == a then b else a
_ -> Nothing
-- | Get instances of a given node.
-- The node is specified through its UUID.
-- The secondary calculation is expensive and frequently called, so optimise
-- this to allocate fewer temporary values
getNodeInstances :: ConfigData -> String -> ([Instance], [Instance])
getNodeInstances cfg nname =
let all_insts = M.elems . fromContainer . configInstances $ cfg
all_disks = fromContainer . configDisks $ cfg
pri_inst = filter ((== Just nname) . instPrimaryNode) $ all_insts
find_disk :: String -> Maybe Disk
find_disk d_uuid = M.lookup (UTF8.fromString d_uuid) all_disks
inst_disks :: [(Instance, [Disk])]
inst_disks = [(i, mapMaybe find_disk $ instDisks i) | i <- all_insts]
sec_insts :: [Instance]
sec_insts = [inst |
(inst, disks) <- inst_disks,
s_uuid <- mapMaybe (\d -> (instPrimaryNode inst) >>= (computeDiskSecondaryNode d)) disks,
s_uuid == nname]
in (pri_inst, sec_insts)
-- | Computes the role of a node.
getNodeRole :: ConfigData -> Node -> NodeRole
getNodeRole cfg node
| uuidOf node == clusterMasterNode (configCluster cfg) = NRMaster
| nodeMasterCandidate node = NRCandidate
| nodeDrained node = NRDrained
| nodeOffline node = NROffline
| otherwise = NRRegular
-- | Get the list of the master nodes (usually one).
getMasterNodes :: ConfigData -> [Node]
getMasterNodes cfg =
filter ((==) NRMaster . getNodeRole cfg) . F.toList . configNodes $ cfg
-- | Get the list of master candidates, /not including/ the master itself.
getMasterCandidates :: ConfigData -> [Node]
getMasterCandidates cfg =
filter ((==) NRCandidate . getNodeRole cfg) . F.toList . configNodes $ cfg
-- | Get the list of master candidates, /including/ the master.
getMasterOrCandidates :: ConfigData -> [Node]
getMasterOrCandidates cfg =
let isMC r = (r == NRCandidate) || (r == NRMaster)
in filter (isMC . getNodeRole cfg) . F.toList . configNodes $ cfg
-- | Get the network parameters for the master IP address.
getMasterNetworkParameters :: ConfigData -> MasterNetworkParameters
getMasterNetworkParameters cfg =
let cluster = configCluster cfg
in MasterNetworkParameters
{ masterNetworkParametersUuid = clusterMasterNode cluster
, masterNetworkParametersIp = clusterMasterIp cluster
, masterNetworkParametersNetmask = clusterMasterNetmask cluster
, masterNetworkParametersNetdev = clusterMasterNetdev cluster
, masterNetworkParametersIpFamily = clusterPrimaryIpFamily cluster
}
-- | Get the list of online nodes.
getOnlineNodes :: ConfigData -> [Node]
getOnlineNodes = filter (not . nodeOffline) . F.toList . configNodes
-- | Returns the default cluster link.
getDefaultNicLink :: ConfigData -> String
getDefaultNicLink =
let ppDefault = UTF8.fromString C.ppDefault
in nicpLink . (M.! ppDefault) . fromContainer
. clusterNicparams . configCluster
-- | Returns the default cluster hypervisor.
getDefaultHypervisor :: ConfigData -> Hypervisor
getDefaultHypervisor cfg =
case clusterEnabledHypervisors $ configCluster cfg of
-- FIXME: this case shouldn't happen (configuration broken), but
-- for now we handle it here because we're not authoritative for
-- the config
[] -> XenPvm
x:_ -> x
-- | Returns instances of a given link.
getInstancesIpByLink :: LinkIpMap -> String -> [String]
getInstancesIpByLink linkipmap link =
M.keys $ M.findWithDefault M.empty link linkipmap
-- | Generic lookup function that converts from a possible abbreviated
-- name to a full name.
getItem :: String -> String -> M.Map String a -> ErrorResult a
getItem kind name allitems = do
let lresult = lookupName (M.keys allitems) name
err msg = Bad $ OpPrereqError (kind ++ " name " ++ name ++ " " ++ msg)
ECodeNoEnt
fullname <- case lrMatchPriority lresult of
PartialMatch -> Ok $ lrContent lresult
ExactMatch -> Ok $ lrContent lresult
MultipleMatch -> err "has multiple matches"
FailMatch -> err "not found"
maybe (err "not found after successfull match?!") Ok $
M.lookup fullname allitems
-- | Simple lookup function, insisting on exact matches and using
-- byte strings.
getItem' :: String -> String -> M.Map BS.ByteString a -> ErrorResult a
getItem' kind name allitems =
let name' = UTF8.fromString name
err = Bad $ OpPrereqError (kind ++ " uuid " ++ name ++ " not found")
ECodeNoEnt
in maybe err Ok $ M.lookup name' allitems
-- | Looks up a node by name or uuid.
getNode :: ConfigData -> String -> ErrorResult Node
getNode cfg name =
let nodes = fromContainer (configNodes cfg)
in case getItem' "Node" name nodes of
-- if not found by uuid, we need to look it up by name
Ok node -> Ok node
Bad _ -> let by_name = M.mapKeys
(nodeName . (M.!) nodes) nodes
in getItem "Node" name by_name
-- | Looks up an instance by name or uuid.
getInstance :: ConfigData -> String -> ErrorResult Instance
getInstance cfg name =
let instances = fromContainer (configInstances cfg)
in case getItem' "Instance" name instances of
-- if not found by uuid, we need to look it up by name
Ok inst -> Ok inst
Bad _ -> let by_name =
M.delete ""
. M.mapKeys (fromMaybe "" . instName . (M.!) instances)
$ instances
in getItem "Instance" name by_name
-- | Looks up an instance by exact name match
getInstanceByName :: ConfigData -> String -> ErrorResult Instance
getInstanceByName cfg name =
let instances = M.elems . fromContainer . configInstances $ cfg
matching = F.find (maybe False (== name) . instName) instances
in case matching of
Just inst -> Ok inst
Nothing -> Bad $ OpPrereqError
("Instance name " ++ name ++ " not found")
ECodeNoEnt
-- | Looks up a disk by uuid.
getDisk :: ConfigData -> String -> ErrorResult Disk
getDisk cfg name =
let disks = fromContainer (configDisks cfg)
in getItem' "Disk" name disks
-- | Looks up a filter by uuid.
getFilterRule :: ConfigData -> String -> ErrorResult FilterRule
getFilterRule cfg name =
let filters = fromContainer (configFilters cfg)
in getItem' "Filter" name filters
-- | Looks up a node group by name or uuid.
getGroup :: ConfigData -> String -> ErrorResult NodeGroup
getGroup cfg name =
let groups = fromContainer (configNodegroups cfg)
in case getItem' "NodeGroup" name groups of
-- if not found by uuid, we need to look it up by name, slow
Ok grp -> Ok grp
Bad _ -> let by_name = M.mapKeys
(groupName . (M.!) groups) groups
in getItem "NodeGroup" name by_name
-- | Computes a node group's node params.
getGroupNdParams :: ConfigData -> NodeGroup -> FilledNDParams
getGroupNdParams cfg ng =
fillParams (clusterNdparams $ configCluster cfg) (groupNdparams ng)
-- | Computes a node group's ipolicy.
getGroupIpolicy :: ConfigData -> NodeGroup -> FilledIPolicy
getGroupIpolicy cfg ng =
fillParams (clusterIpolicy $ configCluster cfg) (groupIpolicy ng)
-- | Computes a group\'s (merged) disk params.
getGroupDiskParams :: ConfigData -> NodeGroup -> GroupDiskParams
getGroupDiskParams cfg ng =
GenericContainer $
fillDict (fromContainer . clusterDiskparams $ configCluster cfg)
(fromContainer $ groupDiskparams ng) []
-- | Get nodes of a given node group.
getGroupNodes :: ConfigData -> String -> [Node]
getGroupNodes cfg gname =
let all_nodes = M.elems . fromContainer . configNodes $ cfg in
filter ((==gname) . nodeGroup) all_nodes
-- | Get (primary, secondary) instances of a given node group.
getGroupInstances :: ConfigData -> String -> ([Instance], [Instance])
getGroupInstances cfg gname =
let gnodes = map uuidOf (getGroupNodes cfg gname)
ginsts = map (getNodeInstances cfg) gnodes in
(concatMap fst ginsts, concatMap snd ginsts)
-- | default FilledHvStateParams.
defaultHvStateParams :: FilledHvStateParams
defaultHvStateParams = FilledHvStateParams
{ hvstateCpuNode = CU.hvstDefaultCpuNode
, hvstateCpuTotal = CU.hvstDefaultCpuTotal
, hvstateMemHv = CU.hvstDefaultMemoryHv
, hvstateMemNode = CU.hvstDefaultMemoryNode
, hvstateMemTotal = CU.hvstDefaultMemoryTotal
}
-- | Retrieves the node's static hypervisor state parameters, missing values
-- filled with group's parameters, missing group parameters are filled
-- with cluster's parameters. Currently, returns hvstate parameters only for
-- the default hypervisor.
getFilledHvStateParams :: ConfigData -> Node -> FilledHvState
getFilledHvStateParams cfg n =
let cluster_hv_state =
fromContainer . clusterHvStateStatic $ configCluster cfg
def_hv = getDefaultHypervisor cfg
cluster_fv = fromMaybe defaultHvStateParams $ M.lookup def_hv
cluster_hv_state
group_fv = case getGroupOfNode cfg n >>=
M.lookup def_hv . fromContainer . groupHvStateStatic of
Just pv -> fillParams cluster_fv pv
Nothing -> cluster_fv
node_fv = case M.lookup def_hv . fromContainer $ nodeHvStateStatic n of
Just pv -> fillParams group_fv pv
Nothing -> group_fv
in GenericContainer $ M.fromList [(def_hv, node_fv)]
-- | Retrieves the instance hypervisor params, missing values filled with
-- cluster defaults.
getFilledInstHvParams :: [String] -> ConfigData -> Instance -> HvParams
getFilledInstHvParams globals cfg inst =
-- First get the defaults of the parent
let maybeHvName = instHypervisor inst
hvParamMap = fromContainer . clusterHvparams $ configCluster cfg
parentHvParams =
maybe M.empty fromContainer (maybeHvName >>= flip M.lookup hvParamMap)
-- Then the os defaults for the given hypervisor
maybeOsName = UTF8.fromString <$> instOs inst
osParamMap = fromContainer . clusterOsHvp $ configCluster cfg
osHvParamMap =
maybe M.empty (maybe M.empty fromContainer . flip M.lookup osParamMap)
maybeOsName
osHvParams =
maybe M.empty (maybe M.empty fromContainer . flip M.lookup osHvParamMap)
maybeHvName
-- Then the child
childHvParams = fromContainer . instHvparams $ inst
-- Helper function
fillFn con val = fillDict con val $ fmap UTF8.fromString globals
in GenericContainer $ fillFn (fillFn parentHvParams osHvParams) childHvParams
-- | Retrieves the instance backend params, missing values filled with cluster
-- defaults.
getFilledInstBeParams :: ConfigData -> Instance -> ErrorResult FilledBeParams
getFilledInstBeParams cfg inst = do
let beParamMap = fromContainer . clusterBeparams . configCluster $ cfg
parentParams <- getItem' "FilledBeParams" C.ppDefault beParamMap
return $ fillParams parentParams (instBeparams inst)
-- | Retrieves the instance os params, missing values filled with cluster
-- defaults. This does NOT include private and secret parameters.
getFilledInstOsParams :: ConfigData -> Instance -> OsParams
getFilledInstOsParams cfg inst =
let maybeOsLookupName = liftM (takeWhile (/= '+')) (instOs inst)
osParamMap = fromContainer . clusterOsparams $ configCluster cfg
childOsParams = instOsparams inst
in case withMissingParam "Instance without OS"
(flip (getItem' "OsParams") osParamMap)
maybeOsLookupName of
Ok parentOsParams -> GenericContainer $
fillDict (fromContainer parentOsParams)
(fromContainer childOsParams) []
Bad _ -> childOsParams
-- | Looks up an instance's primary node.
getInstPrimaryNode :: ConfigData -> String -> ErrorResult Node
getInstPrimaryNode cfg name =
getInstanceByName cfg name
>>= withMissingParam "Instance without primary node" return . instPrimaryNode
>>= getNode cfg
-- | Retrieves all nodes hosting a DRBD disk
getDrbdDiskNodes :: ConfigData -> Disk -> [Node]
getDrbdDiskNodes cfg disk =
let retrieved = case diskLogicalId disk of
Just (LIDDrbd8 nodeA nodeB _ _ _ _) ->
justOk [getNode cfg nodeA, getNode cfg nodeB]
_ -> []
in retrieved ++ concatMap (getDrbdDiskNodes cfg) (diskChildren disk)
-- | Retrieves all the nodes of the instance.
--
-- As instances not using DRBD can be sent as a parameter as well,
-- the primary node has to be appended to the results.
getInstAllNodes :: ConfigData -> String -> ErrorResult [Node]
getInstAllNodes cfg name = do
inst <- getInstanceByName cfg name
inst_disks <- getInstDisksFromObj cfg inst
let disk_nodes = concatMap (getDrbdDiskNodes cfg) inst_disks
pNode <- getInstPrimaryNode cfg name
return . nub $ pNode:disk_nodes
-- | Get disks for a given instance.
-- The instance is specified by name or uuid.
getInstDisks :: ConfigData -> String -> ErrorResult [Disk]
getInstDisks cfg iname =
getInstance cfg iname >>= mapM (getDisk cfg) . instDisks
-- | Get disks for a given instance object.
getInstDisksFromObj :: ConfigData -> Instance -> ErrorResult [Disk]
getInstDisksFromObj cfg =
getInstDisks cfg . uuidOf
-- | Collects a value for all DRBD disks
collectFromDrbdDisks
:: (Monoid a)
=> (String -> String -> Int -> Int -> Int -> Private DRBDSecret -> a)
-- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
-> Disk -> a
collectFromDrbdDisks f = col
where
col (diskLogicalId &&& diskChildren ->
(Just (LIDDrbd8 nA nB port mA mB secret), ch)) =
f nA nB port mA mB secret <> F.foldMap col ch
col d = F.foldMap col (diskChildren d)
-- | Returns the DRBD secrets of a given 'Disk'
getDrbdSecretsForDisk :: Disk -> [DRBDSecret]
getDrbdSecretsForDisk = collectFromDrbdDisks
(\_ _ _ _ _ (Private secret) -> [secret])
-- | Returns the DRBD minors of a given 'Disk'
getDrbdMinorsForDisk :: Disk -> [(Int, String)]
getDrbdMinorsForDisk =
collectFromDrbdDisks (\nA nB _ mnA mnB _ -> [(mnA, nA), (mnB, nB)])
-- | Filters DRBD minors for a given node.
getDrbdMinorsForNode :: String -> Disk -> [(Int, String)]
getDrbdMinorsForNode node disk =
let child_minors = concatMap (getDrbdMinorsForNode node) (diskChildren disk)
this_minors =
case diskLogicalId disk of
Just (LIDDrbd8 nodeA nodeB _ minorA minorB _)
| nodeA == node -> [(minorA, nodeB)]
| nodeB == node -> [(minorB, nodeA)]
_ -> []
in this_minors ++ child_minors
-- | Returns the DRBD minors of a given instance
getDrbdMinorsForInstance :: ConfigData -> Instance
-> ErrorResult [(Int, String)]
getDrbdMinorsForInstance cfg =
liftM (concatMap getDrbdMinorsForDisk) . getInstDisksFromObj cfg
-- | String for primary role.
rolePrimary :: String
rolePrimary = "primary"
-- | String for secondary role.
roleSecondary :: String
roleSecondary = "secondary"
-- | Gets the list of DRBD minors for an instance that are related to
-- a given node.
getInstMinorsForNode :: ConfigData
-> String -- ^ The UUID of a node.
-> Instance
-> [(String, Int, String, String, String, String)]
getInstMinorsForNode cfg node inst =
let nrole = if Just node == instPrimaryNode inst
then rolePrimary
else roleSecondary
iname = fromMaybe "" $ instName inst
inst_disks = case getInstDisksFromObj cfg inst of
Ok disks -> disks
Bad _ -> []
-- FIXME: the disk/ build there is hack-ish; unify this in a
-- separate place, or reuse the iv_name (but that is deprecated on
-- the Python side)
in concatMap (\(idx, dsk) ->
[(node, minor, iname, "disk/" ++ show idx, nrole, peer)
| (minor, peer) <- getDrbdMinorsForNode node dsk]) .
zip [(0::Int)..] $ inst_disks
-- | Builds link -> ip -> instname map.
-- For instances without a name, we insert the uuid instead.
--
-- TODO: improve this by splitting it into multiple independent functions:
--
-- * abstract the \"fetch instance with filled params\" functionality
--
-- * abstsract the [instance] -> [(nic, instance_name)] part
--
-- * etc.
buildLinkIpInstnameMap :: ConfigData -> LinkIpMap
buildLinkIpInstnameMap cfg =
let cluster = configCluster cfg
instances = M.elems . fromContainer . configInstances $ cfg
defparams = (M.!) (fromContainer $ clusterNicparams cluster)
$ UTF8.fromString C.ppDefault
nics = concatMap (\i -> [(fromMaybe (uuidOf i) $ instName i, nic)
| nic <- instNics i])
instances
in foldl' (\accum (iname, nic) ->
let pparams = nicNicparams nic
fparams = fillParams defparams pparams
link = nicpLink fparams
in case nicIp nic of
Nothing -> accum
Just ip -> let oldipmap = M.findWithDefault M.empty
link accum
newipmap = M.insert ip iname oldipmap
in M.insert link newipmap accum
) M.empty nics
-- | Returns a node's group, with optional failure if we can't find it
-- (configuration corrupt).
getGroupOfNode :: ConfigData -> Node -> Maybe NodeGroup
getGroupOfNode cfg node =
M.lookup (UTF8.fromString $ nodeGroup node)
(fromContainer . configNodegroups $ cfg)
-- | Returns a node's ndparams, filled.
getNodeNdParams :: ConfigData -> Node -> Maybe FilledNDParams
getNodeNdParams cfg node = do
group <- getGroupOfNode cfg node
let gparams = getGroupNdParams cfg group
return $ fillParams gparams (nodeNdparams node)
-- * Network
-- | Looks up a network. If looking up by uuid fails, we look up
-- by name.
getNetwork :: ConfigData -> String -> ErrorResult Network
getNetwork cfg name =
let networks = fromContainer (configNetworks cfg)
in case getItem' "Network" name networks of
Ok net -> Ok net
Bad _ -> let by_name = M.mapKeys
(fromNonEmpty . networkName . (M.!) networks)
networks
in getItem "Network" name by_name
-- ** MACs
type MAC = String
-- | Returns all MAC addresses used in the cluster.
getAllMACs :: ConfigData -> [MAC]
getAllMACs = F.foldMap (map nicMac . instNics) . configInstances
-- ** DRBD secrets
getAllDrbdSecrets :: ConfigData -> [DRBDSecret]
getAllDrbdSecrets = F.foldMap getDrbdSecretsForDisk . configDisks
-- ** LVs
-- | A map from node UUIDs to
--
-- FIXME: After adding designated types for UUIDs,
-- use them to replace 'String' here.
type NodeLVsMap = MM.MultiMap String LogicalVolume
getInstanceLVsByNode :: ConfigData -> Instance -> ErrorResult NodeLVsMap
getInstanceLVsByNode cd inst =
withMissingParam "Instance without Primary Node"
(\i -> return $ MM.fromList . lvsByNode i)
(instPrimaryNode inst)
<*> getInstDisksFromObj cd inst
where
lvsByNode :: String -> [Disk] -> [(String, LogicalVolume)]
lvsByNode node = concatMap (lvsByNode1 node)
lvsByNode1 :: String -> Disk -> [(String, LogicalVolume)]
lvsByNode1 _ (diskLogicalId &&& diskChildren
-> (Just (LIDDrbd8 nA nB _ _ _ _), ch)) =
lvsByNode nA ch ++ lvsByNode nB ch
lvsByNode1 node (diskLogicalId -> (Just (LIDPlain lv))) =
[(node, lv)]
lvsByNode1 node (diskChildren -> ch) = lvsByNode node ch
getAllLVs :: ConfigData -> ErrorResult (S.Set LogicalVolume)
getAllLVs cd = mconcat <$> mapM (liftM MM.values . getInstanceLVsByNode cd)
(F.toList $ configInstances cd)
-- * ND params
-- | Type class denoting objects which have node parameters.
class NdParamObject a where
getNdParamsOf :: ConfigData -> a -> Maybe FilledNDParams
instance NdParamObject Node where
getNdParamsOf = getNodeNdParams
instance NdParamObject NodeGroup where
getNdParamsOf cfg = Just . getGroupNdParams cfg
instance NdParamObject Cluster where
getNdParamsOf _ = Just . clusterNdparams