1 {-| Implementation of the Ganeti configuration database.
7 Copyright (C) 2011, 2012 Google Inc.
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.
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.
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
34 , getDefaultHypervisor
35 , getInstancesIpByLink
46 , getInstMinorsForNode
47 , buildLinkIpInstnameMap
51 import Control.Monad (liftM)
52 import Data.List (foldl')
53 import qualified Data.Map as M
54 import qualified Data.Set as S
55 import qualified Text.JSON as J
57 import Ganeti.BasicTypes
58 import qualified Ganeti.Constants as C
63 -- | Type alias for the link and ip map.
64 type LinkIpMap = M.Map String (M.Map String String)
66 -- | Type class denoting objects which have node parameters.
67 class NdParamObject a where
68 getNdParamsOf :: ConfigData -> a -> Maybe FilledNDParams
70 -- | Reads the config file.
71 readConfig :: FilePath -> IO String
74 -- | Parses the configuration file.
75 parseConfig :: String -> Result ConfigData
76 parseConfig = fromJResult "parsing configuration" . J.decodeStrict
78 -- | Wrapper over 'readConfig' and 'parseConfig'.
79 loadConfig :: FilePath -> IO (Result ConfigData)
80 loadConfig = fmap parseConfig . readConfig
84 -- | Computes the nodes covered by a disk.
85 computeDiskNodes :: Disk -> S.Set String
86 computeDiskNodes dsk =
87 case diskLogicalId dsk of
88 LIDDrbd8 nodeA nodeB _ _ _ _ -> S.fromList [nodeA, nodeB]
91 -- | Computes all disk-related nodes of an instance. For non-DRBD,
92 -- this will be empty, for DRBD it will contain both the primary and
94 instDiskNodes :: Instance -> S.Set String
95 instDiskNodes = S.unions . map computeDiskNodes . instDisks
97 -- | Computes all nodes of an instance.
98 instNodes :: Instance -> S.Set String
99 instNodes inst = instPrimaryNode inst `S.insert` instDiskNodes inst
101 -- | Computes the secondary nodes of an instance. Since this is valid
102 -- only for DRBD, we call directly 'instDiskNodes', skipping over the
103 -- extra primary insert.
104 instSecondaryNodes :: Instance -> S.Set String
105 instSecondaryNodes inst =
106 instPrimaryNode inst `S.delete` instDiskNodes inst
108 -- | Get instances of a given node.
109 getNodeInstances :: ConfigData -> String -> ([Instance], [Instance])
110 getNodeInstances cfg nname =
111 let all_inst = M.elems . fromContainer . configInstances $ cfg
112 pri_inst = filter ((== nname) . instPrimaryNode) all_inst
113 sec_inst = filter ((nname `S.member`) . instSecondaryNodes) all_inst
114 in (pri_inst, sec_inst)
116 -- | Computes the role of a node.
117 getNodeRole :: ConfigData -> Node -> NodeRole
119 | nodeName node == clusterMasterNode (configCluster cfg) = NRMaster
120 | nodeMasterCandidate node = NRCandidate
121 | nodeDrained node = NRDrained
122 | nodeOffline node = NROffline
123 | otherwise = NRRegular
125 -- | Returns the default cluster link.
126 getDefaultNicLink :: ConfigData -> String
128 nicpLink . (M.! C.ppDefault) . fromContainer .
129 clusterNicparams . configCluster
131 -- | Returns the default cluster hypervisor.
132 getDefaultHypervisor :: ConfigData -> Hypervisor
133 getDefaultHypervisor cfg =
134 case clusterEnabledHypervisors $ configCluster cfg of
135 -- FIXME: this case shouldn't happen (configuration broken), but
136 -- for now we handle it here because we're not authoritative for
141 -- | Returns instances of a given link.
142 getInstancesIpByLink :: LinkIpMap -> String -> [String]
143 getInstancesIpByLink linkipmap link =
144 M.keys $ M.findWithDefault M.empty link linkipmap
146 -- | Generic lookup function that converts from a possible abbreviated
147 -- name to a full name.
148 getItem :: String -> String -> M.Map String a -> ErrorResult a
149 getItem kind name allitems = do
150 let lresult = lookupName (M.keys allitems) name
151 err msg = Bad $ OpPrereqError (kind ++ " name " ++ name ++ " " ++ msg)
153 fullname <- case lrMatchPriority lresult of
154 PartialMatch -> Ok $ lrContent lresult
155 ExactMatch -> Ok $ lrContent lresult
156 MultipleMatch -> err "has multiple matches"
157 FailMatch -> err "not found"
158 maybe (err "not found after successfull match?!") Ok $
159 M.lookup fullname allitems
161 -- | Looks up a node.
162 getNode :: ConfigData -> String -> ErrorResult Node
163 getNode cfg name = getItem "Node" name (fromContainer $ configNodes cfg)
165 -- | Looks up an instance.
166 getInstance :: ConfigData -> String -> ErrorResult Instance
167 getInstance cfg name =
168 getItem "Instance" name (fromContainer $ configInstances cfg)
170 -- | Looks up a node group. This is more tricky than for
171 -- node/instances since the groups map is indexed by uuid, not name.
172 getGroup :: ConfigData -> String -> ErrorResult NodeGroup
174 let groups = fromContainer (configNodegroups cfg)
175 in case getItem "NodeGroup" name groups of
176 -- if not found by uuid, we need to look it up by name, slow
178 Bad _ -> let by_name = M.mapKeys
179 (groupName . (M.!) groups) groups
180 in getItem "NodeGroup" name by_name
182 -- | Computes a node group's node params.
183 getGroupNdParams :: ConfigData -> NodeGroup -> FilledNDParams
184 getGroupNdParams cfg ng =
185 fillNDParams (clusterNdparams $ configCluster cfg) (groupNdparams ng)
187 -- | Computes a node group's ipolicy.
188 getGroupIpolicy :: ConfigData -> NodeGroup -> FilledIPolicy
189 getGroupIpolicy cfg ng =
190 fillIPolicy (clusterIpolicy $ configCluster cfg) (groupIpolicy ng)
192 -- | Computes a group\'s (merged) disk params.
193 getGroupDiskParams :: ConfigData -> NodeGroup -> DiskParams
194 getGroupDiskParams cfg ng =
196 fillDict (fromContainer . clusterDiskparams $ configCluster cfg)
197 (fromContainer $ groupDiskparams ng) []
199 -- | Get nodes of a given node group.
200 getGroupNodes :: ConfigData -> String -> [Node]
201 getGroupNodes cfg gname =
202 let all_nodes = M.elems . fromContainer . configNodes $ cfg in
203 filter ((==gname) . nodeGroup) all_nodes
205 -- | Get (primary, secondary) instances of a given node group.
206 getGroupInstances :: ConfigData -> String -> ([Instance], [Instance])
207 getGroupInstances cfg gname =
208 let gnodes = map nodeName (getGroupNodes cfg gname)
209 ginsts = map (getNodeInstances cfg) gnodes in
210 (concatMap fst ginsts, concatMap snd ginsts)
212 -- | Looks up an instance's primary node.
213 getInstPrimaryNode :: ConfigData -> String -> ErrorResult Node
214 getInstPrimaryNode cfg name =
215 liftM instPrimaryNode (getInstance cfg name) >>= getNode cfg
217 -- | Filters DRBD minors for a given node.
218 getDrbdMinorsForNode :: String -> Disk -> [(Int, String)]
219 getDrbdMinorsForNode node disk =
220 let child_minors = concatMap (getDrbdMinorsForNode node) (diskChildren disk)
222 case diskLogicalId disk of
223 LIDDrbd8 nodeA nodeB _ minorA minorB _
224 | nodeA == node -> [(minorA, nodeB)]
225 | nodeB == node -> [(minorB, nodeA)]
227 in this_minors ++ child_minors
229 -- | String for primary role.
230 rolePrimary :: String
231 rolePrimary = "primary"
233 -- | String for secondary role.
234 roleSecondary :: String
235 roleSecondary = "secondary"
237 -- | Gets the list of DRBD minors for an instance that are related to
239 getInstMinorsForNode :: String -> Instance
240 -> [(String, Int, String, String, String, String)]
241 getInstMinorsForNode node inst =
242 let role = if node == instPrimaryNode inst
245 iname = instName inst
246 -- FIXME: the disk/ build there is hack-ish; unify this in a
247 -- separate place, or reuse the iv_name (but that is deprecated on
249 in concatMap (\(idx, dsk) ->
250 [(node, minor, iname, "disk/" ++ show idx, role, peer)
251 | (minor, peer) <- getDrbdMinorsForNode node dsk]) .
252 zip [(0::Int)..] . instDisks $ inst
254 -- | Builds link -> ip -> instname map.
256 -- TODO: improve this by splitting it into multiple independent functions:
258 -- * abstract the \"fetch instance with filled params\" functionality
260 -- * abstsract the [instance] -> [(nic, instance_name)] part
263 buildLinkIpInstnameMap :: ConfigData -> LinkIpMap
264 buildLinkIpInstnameMap cfg =
265 let cluster = configCluster cfg
266 instances = M.elems . fromContainer . configInstances $ cfg
267 defparams = (M.!) (fromContainer $ clusterNicparams cluster) C.ppDefault
268 nics = concatMap (\i -> [(instName i, nic) | nic <- instNics i])
270 in foldl' (\accum (iname, nic) ->
271 let pparams = nicNicparams nic
272 fparams = fillNicParams defparams pparams
273 link = nicpLink fparams
276 Just ip -> let oldipmap = M.findWithDefault M.empty
278 newipmap = M.insert ip iname oldipmap
279 in M.insert link newipmap accum
283 -- | Returns a node's group, with optional failure if we can't find it
284 -- (configuration corrupt).
285 getGroupOfNode :: ConfigData -> Node -> Maybe NodeGroup
286 getGroupOfNode cfg node =
287 M.lookup (nodeGroup node) (fromContainer . configNodegroups $ cfg)
289 -- | Returns a node's ndparams, filled.
290 getNodeNdParams :: ConfigData -> Node -> Maybe FilledNDParams
291 getNodeNdParams cfg node = do
292 group <- getGroupOfNode cfg node
293 let gparams = getGroupNdParams cfg group
294 return $ fillNDParams gparams (nodeNdparams node)
296 instance NdParamObject Node where
297 getNdParamsOf = getNodeNdParams
299 instance NdParamObject NodeGroup where
300 getNdParamsOf cfg = Just . getGroupNdParams cfg
302 instance NdParamObject Cluster where
303 getNdParamsOf _ = Just . clusterNdparams