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