Revision 212b66c3
b/src/Ganeti/Common.hs | ||
---|---|---|
45 | 45 |
, parseOptsInner |
46 | 46 |
, parseOptsCmds |
47 | 47 |
, genericMainCmds |
48 |
, fillUpList |
|
49 |
, fillPairFromMaybe |
|
50 |
, pickPairUnique |
|
48 | 51 |
) where |
49 | 52 |
|
50 | 53 |
import Control.Monad (foldM) |
... | ... | |
341 | 344 |
(opts, args, fn) <- |
342 | 345 |
parseOptsCmds defaults cmd_args prog personalities genopts |
343 | 346 |
fn opts args |
347 |
|
|
348 |
-- | Order a list of pairs in the order of the given list and fill up |
|
349 |
-- the list for elements that don't have a matching pair |
|
350 |
fillUpList :: ([(a, b)] -> a -> (a, b)) -> [a] -> [(a, b)] -> [(a, b)] |
|
351 |
fillUpList fill_fn inputs pairs = |
|
352 |
map (fill_fn pairs) inputs |
|
353 |
|
|
354 |
-- | Fill up a pair with fillup element if no matching pair is present |
|
355 |
fillPairFromMaybe :: (a -> (a, b)) -> (a -> [(a, b)] -> Maybe (a, b)) |
|
356 |
-> [(a, b)] -> a -> (a, b) |
|
357 |
fillPairFromMaybe fill_fn pick_fn pairs element = fromMaybe (fill_fn element) |
|
358 |
(pick_fn element pairs) |
|
359 |
|
|
360 |
-- | Check if the given element matches the given pair |
|
361 |
isMatchingPair :: (Eq a) => a -> (a, b) -> Bool |
|
362 |
isMatchingPair element (pair_element, _) = element == pair_element |
|
363 |
|
|
364 |
-- | Pick a specific element's pair from the list |
|
365 |
pickPairUnique :: (Eq a) => a -> [(a, b)] -> Maybe (a, b) |
|
366 |
pickPairUnique element pairs = |
|
367 |
let res = filter (isMatchingPair element) pairs |
|
368 |
in case res of |
|
369 |
[x] -> Just x |
|
370 |
-- if we have more than one result, we should get suspcious |
|
371 |
_ -> Nothing |
b/src/Ganeti/Query/Node.hs | ||
---|---|---|
36 | 36 |
import qualified Text.JSON as J |
37 | 37 |
|
38 | 38 |
import Ganeti.Config |
39 |
import Ganeti.Common |
|
39 | 40 |
import Ganeti.Objects |
40 | 41 |
import Ganeti.JSON |
41 | 42 |
import Ganeti.Rpc |
... | ... | |
228 | 229 |
fieldsMap = |
229 | 230 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) nodeFields |
230 | 231 |
|
232 |
-- | Create an RPC result for a broken node |
|
233 |
rpcResultNodeBroken :: Node -> (Node, Runtime) |
|
234 |
rpcResultNodeBroken node = (node, Left (RpcResultError "Broken configuration")) |
|
235 |
|
|
231 | 236 |
-- | Collect live data from RPC query if enabled. |
232 | 237 |
-- |
233 | 238 |
-- FIXME: Check which fields we actually need and possibly send empty |
... | ... | |
237 | 242 |
collectLiveData False _ nodes = |
238 | 243 |
return $ zip nodes (repeat $ Left (RpcResultError "Live data disabled")) |
239 | 244 |
collectLiveData True cfg nodes = do |
240 |
let storage_units = getClusterStorageUnits cfg |
|
241 |
hvs = [getDefaultHypervisorSpec cfg] |
|
242 |
step n (bn, gn, em) = |
|
243 |
let ndp' = getNodeNdParams cfg n |
|
244 |
in case ndp' of |
|
245 |
Just ndp -> (bn, n : gn, |
|
246 |
(nodeName n, ndpExclusiveStorage ndp) : em) |
|
247 |
Nothing -> (n : bn, gn, em) |
|
248 |
(bnodes, gnodes, emap) = foldr step ([], [], []) nodes |
|
249 |
rpcres <- executeRpcCall gnodes (RpcCallNodeInfo storage_units hvs |
|
250 |
(Map.fromList emap)) |
|
251 |
-- FIXME: The order of nodes in the result could be different from the input |
|
252 |
return $ zip bnodes (repeat $ Left (RpcResultError "Broken configuration")) |
|
253 |
++ rpcres |
|
245 |
let hvs = [getDefaultHypervisorSpec cfg] |
|
246 |
good_nodes = nodesWithValidConfig cfg nodes |
|
247 |
storage_units = getStorageUnitsOfNodes cfg good_nodes |
|
248 |
rpcres <- executeRpcCall good_nodes (RpcCallNodeInfo storage_units hvs) |
|
249 |
return $ fillUpList (fillPairFromMaybe rpcResultNodeBroken pickPairUnique) |
|
250 |
nodes rpcres |
|
254 | 251 |
|
255 | 252 |
-- | Looks up the default hypervisor and it's hvparams |
256 | 253 |
getDefaultHypervisorSpec :: ConfigData -> (Hypervisor, HvParams) |
b/src/Ganeti/Rpc.hs | ||
---|---|---|
337 | 337 |
-- | NodeInfo |
338 | 338 |
-- Return node information. |
339 | 339 |
$(buildObject "RpcCallNodeInfo" "rpcCallNodeInfo" |
340 |
[ simpleField "storage_units" [t| [ (StorageType, String) ] |]
|
|
340 |
[ simpleField "storage_units" [t| Map.Map String [StorageUnit] |]
|
|
341 | 341 |
, simpleField "hypervisors" [t| [ (Hypervisor, HvParams) ] |] |
342 |
, simpleField "exclusive_storage" [t| Map.Map String Bool |] |
|
343 | 342 |
]) |
344 | 343 |
|
345 | 344 |
$(buildObject "StorageInfo" "storageInfo" |
... | ... | |
371 | 370 |
rpcCallTimeout _ = rpcTimeoutToRaw Urgent |
372 | 371 |
rpcCallAcceptOffline _ = False |
373 | 372 |
rpcCallData n call = J.encode |
374 |
( rpcCallNodeInfoStorageUnits call |
|
375 |
, rpcCallNodeInfoHypervisors call |
|
376 |
, fromMaybe (error $ "Programmer error: missing parameter for node named " |
|
373 |
( fromMaybe (error $ "Programmer error: missing parameter for node named " |
|
377 | 374 |
++ nodeName n) |
378 |
$ Map.lookup (nodeName n) (rpcCallNodeInfoExclusiveStorage call) |
|
375 |
$ Map.lookup (nodeUuid n) (rpcCallNodeInfoStorageUnits call) |
|
376 |
, rpcCallNodeInfoHypervisors call |
|
379 | 377 |
) |
380 | 378 |
|
381 | 379 |
instance Rpc RpcCallNodeInfo RpcResultNodeInfo where |
b/src/Ganeti/Storage/Utils.hs | ||
---|---|---|
24 | 24 |
-} |
25 | 25 |
|
26 | 26 |
module Ganeti.Storage.Utils |
27 |
( getClusterStorageUnits |
|
27 |
( getStorageUnitsOfNodes |
|
28 |
, nodesWithValidConfig |
|
28 | 29 |
) where |
29 | 30 |
|
31 |
import Ganeti.Config |
|
30 | 32 |
import Ganeti.Objects |
31 | 33 |
import Ganeti.Types |
32 | 34 |
import qualified Ganeti.Types as T |
33 | 35 |
|
34 |
type StorageUnit = (StorageType, String) |
|
36 |
import Control.Monad |
|
37 |
import Data.Maybe |
|
38 |
import qualified Data.Map as M |
|
35 | 39 |
|
36 | 40 |
-- | Get the cluster's default storage unit for a given disk template |
37 |
getDefaultStorageKey :: ConfigData -> DiskTemplate -> Maybe String
|
|
41 |
getDefaultStorageKey :: ConfigData -> DiskTemplate -> Maybe StorageKey
|
|
38 | 42 |
getDefaultStorageKey cfg T.DTDrbd8 = clusterVolumeGroupName $ configCluster cfg |
39 | 43 |
getDefaultStorageKey cfg T.DTPlain = clusterVolumeGroupName $ configCluster cfg |
40 | 44 |
getDefaultStorageKey cfg T.DTFile = |
... | ... | |
44 | 48 |
getDefaultStorageKey _ _ = Nothing |
45 | 49 |
|
46 | 50 |
-- | Get the cluster's default spindle storage unit |
47 |
getDefaultSpindleSU :: ConfigData -> (StorageType, Maybe String)
|
|
51 |
getDefaultSpindleSU :: ConfigData -> (StorageType, Maybe StorageKey)
|
|
48 | 52 |
getDefaultSpindleSU cfg = |
49 | 53 |
(T.StorageLvmPv, clusterVolumeGroupName $ configCluster cfg) |
50 | 54 |
|
51 | 55 |
-- | Get the cluster's storage units from the configuration |
52 |
getClusterStorageUnits :: ConfigData -> [StorageUnit]
|
|
53 |
getClusterStorageUnits cfg = foldSUs (maybe_units ++ [spindle_unit]) |
|
56 |
getClusterStorageUnitRaws :: ConfigData -> [StorageUnitRaw]
|
|
57 |
getClusterStorageUnitRaws cfg = foldSUs (maybe_units ++ [spindle_unit])
|
|
54 | 58 |
where disk_templates = clusterEnabledDiskTemplates $ configCluster cfg |
55 | 59 |
storage_types = map diskTemplateToStorageType disk_templates |
56 | 60 |
maybe_units = zip storage_types (map (getDefaultStorageKey cfg) |
... | ... | |
58 | 62 |
spindle_unit = getDefaultSpindleSU cfg |
59 | 63 |
|
60 | 64 |
-- | fold the storage unit list by sorting out the ones without keys |
61 |
foldSUs :: [(StorageType, Maybe String)] -> [StorageUnit]
|
|
65 |
foldSUs :: [(StorageType, Maybe StorageKey)] -> [StorageUnitRaw]
|
|
62 | 66 |
foldSUs = foldr ff [] |
63 |
where ff (st, Just sk) acc = (st, sk) : acc
|
|
67 |
where ff (st, Just sk) acc = SURaw st sk : acc
|
|
64 | 68 |
ff (_, Nothing) acc = acc |
65 | 69 |
|
66 |
-- | Mapping fo disk templates to storage type |
|
67 |
-- FIXME: This is semantically the same as the constant |
|
68 |
-- C.diskTemplatesStorageType |
|
69 |
diskTemplateToStorageType :: DiskTemplate -> StorageType |
|
70 |
diskTemplateToStorageType T.DTExt = T.StorageExt |
|
71 |
diskTemplateToStorageType T.DTFile = T.StorageFile |
|
72 |
diskTemplateToStorageType T.DTSharedFile = T.StorageFile |
|
73 |
diskTemplateToStorageType T.DTDrbd8 = T.StorageLvmVg |
|
74 |
diskTemplateToStorageType T.DTPlain = T.StorageLvmVg |
|
75 |
diskTemplateToStorageType T.DTRbd = T.StorageRados |
|
76 |
diskTemplateToStorageType T.DTDiskless = T.StorageDiskless |
|
77 |
diskTemplateToStorageType T.DTBlock = T.StorageBlock |
|
70 |
-- | Gets the value of the 'exclusive storage' flag of the node |
|
71 |
getExclusiveStorage :: ConfigData -> Node -> Maybe Bool |
|
72 |
getExclusiveStorage cfg n = liftM ndpExclusiveStorage (getNodeNdParams cfg n) |
|
73 |
|
|
74 |
-- | Determines whether a node's config contains an 'exclusive storage' flag |
|
75 |
hasExclusiveStorageFlag :: ConfigData -> Node -> Bool |
|
76 |
hasExclusiveStorageFlag cfg = isJust . getExclusiveStorage cfg |
|
77 |
|
|
78 |
-- | Filter for nodes with a valid config |
|
79 |
nodesWithValidConfig :: ConfigData -> [Node] -> [Node] |
|
80 |
nodesWithValidConfig cfg = filter (hasExclusiveStorageFlag cfg) |
|
81 |
|
|
82 |
-- | Get the storage units of the node |
|
83 |
getStorageUnitsOfNode :: ConfigData -> Node -> [StorageUnit] |
|
84 |
getStorageUnitsOfNode cfg n = |
|
85 |
let clusterSUs = getClusterStorageUnitRaws cfg |
|
86 |
es = fromJust (getExclusiveStorage cfg n) |
|
87 |
in map (addParamsToStorageUnit es) clusterSUs |
|
88 |
|
|
89 |
-- | Get the storage unit map for all nodes |
|
90 |
getStorageUnitsOfNodes :: ConfigData -> [Node] -> M.Map String [StorageUnit] |
|
91 |
getStorageUnitsOfNodes cfg ns = |
|
92 |
M.fromList (map (\n -> (nodeUuid n, getStorageUnitsOfNode cfg n)) ns) |
b/src/Ganeti/Types.hs | ||
---|---|---|
63 | 63 |
, hypervisorToRaw |
64 | 64 |
, OobCommand(..) |
65 | 65 |
, StorageType(..) |
66 |
, storageTypeToRaw |
|
66 | 67 |
, NodeEvacMode(..) |
67 | 68 |
, FileDriver(..) |
68 | 69 |
, InstCreateMode(..) |
... | ... | |
95 | 96 |
, ELogType(..) |
96 | 97 |
, ReasonElem |
97 | 98 |
, ReasonTrail |
99 |
, StorageUnit(..) |
|
100 |
, StorageUnitRaw(..) |
|
101 |
, StorageKey |
|
102 |
, addParamsToStorageUnit |
|
103 |
, diskTemplateToStorageType |
|
98 | 104 |
) where |
99 | 105 |
|
100 | 106 |
import Control.Monad (liftM) |
... | ... | |
311 | 317 |
]) |
312 | 318 |
$(THH.makeJSONInstance ''StorageType) |
313 | 319 |
|
320 |
-- | Storage keys are identifiers for storage units. Their content varies |
|
321 |
-- depending on the storage type, for example a storage key for LVM storage |
|
322 |
-- is the volume group name. |
|
323 |
type StorageKey = String |
|
324 |
|
|
325 |
-- | Storage parameters |
|
326 |
type SPExclusiveStorage = Bool |
|
327 |
|
|
328 |
-- | Storage units without storage-type-specific parameters |
|
329 |
data StorageUnitRaw = SURaw StorageType StorageKey |
|
330 |
|
|
331 |
-- | Full storage unit with storage-type-specific parameters |
|
332 |
data StorageUnit = SUFile StorageKey |
|
333 |
| SULvmPv StorageKey SPExclusiveStorage |
|
334 |
| SULvmVg StorageKey SPExclusiveStorage |
|
335 |
| SUDiskless StorageKey |
|
336 |
| SUBlock StorageKey |
|
337 |
| SURados StorageKey |
|
338 |
| SUExt StorageKey |
|
339 |
deriving (Eq) |
|
340 |
|
|
341 |
instance Show StorageUnit where |
|
342 |
show (SUFile key) = showSUSimple StorageFile key |
|
343 |
show (SULvmPv key es) = showSULvm StorageLvmPv key es |
|
344 |
show (SULvmVg key es) = showSULvm StorageLvmVg key es |
|
345 |
show (SUDiskless key) = showSUSimple StorageDiskless key |
|
346 |
show (SUBlock key) = showSUSimple StorageBlock key |
|
347 |
show (SURados key) = showSUSimple StorageRados key |
|
348 |
show (SUExt key) = showSUSimple StorageExt key |
|
349 |
|
|
350 |
instance JSON StorageUnit where |
|
351 |
showJSON (SUFile key) = showJSON (StorageFile, key, []::[String]) |
|
352 |
showJSON (SULvmPv key es) = showJSON (StorageLvmPv, key, [es]) |
|
353 |
showJSON (SULvmVg key es) = showJSON (StorageLvmVg, key, [es]) |
|
354 |
showJSON (SUDiskless key) = showJSON (StorageDiskless, key, []::[String]) |
|
355 |
showJSON (SUBlock key) = showJSON (StorageBlock, key, []::[String]) |
|
356 |
showJSON (SURados key) = showJSON (StorageRados, key, []::[String]) |
|
357 |
showJSON (SUExt key) = showJSON (StorageExt, key, []::[String]) |
|
358 |
-- FIXME: add readJSON implementation |
|
359 |
readJSON = fail "Not implemented" |
|
360 |
|
|
361 |
-- | Composes a string representation of storage types without |
|
362 |
-- storage parameters |
|
363 |
showSUSimple :: StorageType -> StorageKey -> String |
|
364 |
showSUSimple st sk = show (storageTypeToRaw st, sk, []::[String]) |
|
365 |
|
|
366 |
-- | Composes a string representation of the LVM storage types |
|
367 |
showSULvm :: StorageType -> StorageKey -> SPExclusiveStorage -> String |
|
368 |
showSULvm st sk es = show (storageTypeToRaw st, sk, [es]) |
|
369 |
|
|
370 |
-- | Mapping fo disk templates to storage type |
|
371 |
-- FIXME: This is semantically the same as the constant |
|
372 |
-- C.diskTemplatesStorageType, remove this when python constants |
|
373 |
-- are generated from haskell constants |
|
374 |
diskTemplateToStorageType :: DiskTemplate -> StorageType |
|
375 |
diskTemplateToStorageType DTExt = StorageExt |
|
376 |
diskTemplateToStorageType DTFile = StorageFile |
|
377 |
diskTemplateToStorageType DTSharedFile = StorageFile |
|
378 |
diskTemplateToStorageType DTDrbd8 = StorageLvmVg |
|
379 |
diskTemplateToStorageType DTPlain = StorageLvmVg |
|
380 |
diskTemplateToStorageType DTRbd = StorageRados |
|
381 |
diskTemplateToStorageType DTDiskless = StorageDiskless |
|
382 |
diskTemplateToStorageType DTBlock = StorageBlock |
|
383 |
|
|
384 |
-- | Equips a raw storage unit with its parameters |
|
385 |
addParamsToStorageUnit :: SPExclusiveStorage -> StorageUnitRaw -> StorageUnit |
|
386 |
addParamsToStorageUnit _ (SURaw StorageBlock key) = SUBlock key |
|
387 |
addParamsToStorageUnit _ (SURaw StorageDiskless key) = SUDiskless key |
|
388 |
addParamsToStorageUnit _ (SURaw StorageExt key) = SUExt key |
|
389 |
addParamsToStorageUnit _ (SURaw StorageFile key) = SUFile key |
|
390 |
addParamsToStorageUnit es (SURaw StorageLvmPv key) = SULvmPv key es |
|
391 |
addParamsToStorageUnit es (SURaw StorageLvmVg key) = SULvmVg key es |
|
392 |
addParamsToStorageUnit _ (SURaw StorageRados key) = SURados key |
|
393 |
|
|
314 | 394 |
-- | Node evac modes. |
315 | 395 |
$(THH.declareSADT "NodeEvacMode" |
316 | 396 |
[ ("NEvacPrimary", 'C.iallocatorNevacPri) |
b/test/hs/Test/Ganeti/Rpc.hs | ||
---|---|---|
42 | 42 |
import qualified Ganeti.Objects as Objects |
43 | 43 |
import qualified Ganeti.Types as Types |
44 | 44 |
import qualified Ganeti.JSON as JSON |
45 |
import Ganeti.Types |
|
45 | 46 |
|
46 | 47 |
instance Arbitrary Rpc.RpcCallAllInstancesInfo where |
47 | 48 |
arbitrary = Rpc.RpcCallAllInstancesInfo <$> arbitrary |
... | ... | |
50 | 51 |
arbitrary = Rpc.RpcCallInstanceList <$> arbitrary |
51 | 52 |
|
52 | 53 |
instance Arbitrary Rpc.RpcCallNodeInfo where |
53 |
arbitrary = Rpc.RpcCallNodeInfo <$> arbitrary <*> genHvSpecs <*> |
|
54 |
pure Map.empty |
|
54 |
arbitrary = Rpc.RpcCallNodeInfo <$> genStorageUnitMap <*> genHvSpecs |
|
55 |
|
|
56 |
genStorageUnit :: Gen StorageUnit |
|
57 |
genStorageUnit = do |
|
58 |
storage_type <- arbitrary |
|
59 |
storage_key <- genName |
|
60 |
storage_es <- arbitrary |
|
61 |
return $ addParamsToStorageUnit storage_es (SURaw storage_type storage_key) |
|
62 |
|
|
63 |
genStorageUnits :: Gen [StorageUnit] |
|
64 |
genStorageUnits = do |
|
65 |
num_storage_units <- choose (0, 5) |
|
66 |
vectorOf num_storage_units genStorageUnit |
|
67 |
|
|
68 |
genStorageUnitMap :: Gen (Map.Map String [StorageUnit]) |
|
69 |
genStorageUnitMap = do |
|
70 |
num_nodes <- choose (0,5) |
|
71 |
node_uuids <- vectorOf num_nodes genName |
|
72 |
storage_units_list <- vectorOf num_nodes genStorageUnits |
|
73 |
return $ Map.fromList (zip node_uuids storage_units_list) |
|
55 | 74 |
|
56 | 75 |
-- | Generate hypervisor specifications to be used for the NodeInfo call |
57 | 76 |
genHvSpecs :: Gen [ (Types.Hypervisor, Objects.HvParams) ] |
Also available in: Unified diff