Revision 9491766c

b/src/Ganeti/BasicTypes.hs
30 30
  , FromString(..)
31 31
  , isOk
32 32
  , isBad
33
  , justOk
33 34
  , eitherToResult
34 35
  , annotateResult
35 36
  , iterateOk
......
135 136
isBad :: GenericResult a b -> Bool
136 137
isBad = not . isOk
137 138

  
139
-- | Simple filter returning only OK values of GenericResult
140
justOk :: [GenericResult a b] -> [b]
141
justOk [] = []
142
justOk (x:xs) = case x of
143
  Ok  v -> v:justOk xs
144
  Bad _ -> justOk xs
145

  
138 146
-- | Converter from Either to 'GenericResult'.
139 147
eitherToResult :: Either a b -> GenericResult a b
140 148
eitherToResult (Left  s) = Bad s
b/src/Ganeti/Config.hs
44 44
    , getGroupOfNode
45 45
    , getInstPrimaryNode
46 46
    , getInstMinorsForNode
47
    , getInstAllNodes
47 48
    , getNetwork
48 49
    , buildLinkIpInstnameMap
49 50
    , instNodes
50 51
    ) where
51 52

  
52 53
import Control.Monad (liftM)
53
import Data.List (foldl')
54
import Data.List (foldl', nub)
54 55
import qualified Data.Map as M
55 56
import qualified Data.Set as S
56 57
import qualified Text.JSON as J
......
241 242
getInstPrimaryNode cfg name =
242 243
  liftM instPrimaryNode (getInstance cfg name) >>= getNode cfg
243 244

  
245
-- | Retrieves all nodes hosting a DRBD disk
246
getDrbdDiskNodes :: ConfigData -> Disk -> [Node]
247
getDrbdDiskNodes cfg disk =
248
  let retrieved = case diskLogicalId disk of
249
                    LIDDrbd8 nodeA nodeB _ _ _ _ ->
250
                      justOk [getNode cfg nodeA, getNode cfg nodeB]
251
                    _                            -> []
252
  in retrieved ++ concatMap (getDrbdDiskNodes cfg) (diskChildren disk)
253

  
254
-- | Retrieves all the nodes of the instance.
255
--
256
-- As instances not using DRBD can be sent as a parameter as well,
257
-- the primary node has to be appended to the results.
258
getInstAllNodes :: ConfigData -> String -> ErrorResult [Node]
259
getInstAllNodes cfg name = do
260
  inst <- getInstance cfg name
261
  let diskNodes = concatMap (getDrbdDiskNodes cfg) $ instDisks inst
262
  pNode <- getInstPrimaryNode cfg name
263
  return . nub $ pNode:diskNodes
264

  
244 265
-- | Filters DRBD minors for a given node.
245 266
getDrbdMinorsForNode :: String -> Disk -> [(Int, String)]
246 267
getDrbdMinorsForNode node disk =
b/src/Ganeti/Errors.hs
37 37
  , errorExitCode
38 38
  , excName
39 39
  , formatError
40
  , maybeToError
40 41
  ) where
41 42

  
42 43
import Text.JSON hiding (Result, Ok)
......
177 178
errToResult :: ErrorResult a -> Result a
178 179
errToResult (Ok a)  = Ok a
179 180
errToResult (Bad e) = Bad $ formatError e
181

  
182
-- | Convert from a 'Maybe' to a an 'ErrorResult'.
183
maybeToError :: String -> Maybe a -> ErrorResult a
184
maybeToError _ (Just a) = Ok a
185
maybeToError m  Nothing = Bad $ GenericError m
b/src/Ganeti/Query/Common.hs
30 30
  , rsNormal
31 31
  , rsMaybeNoData
32 32
  , rsMaybeUnavail
33
  , rsErrorNoData
33 34
  , rsUnknown
34 35
  , missingRuntime
35 36
  , rpcErrorToStatus
......
47 48
import Data.Maybe (fromMaybe)
48 49
import Text.JSON (JSON, showJSON)
49 50

  
51
import Ganeti.BasicTypes
50 52
import qualified Ganeti.Constants as C
51 53
import Ganeti.Config
54
import Ganeti.Errors
52 55
import Ganeti.JSON
53 56
import Ganeti.Objects
54 57
import Ganeti.Rpc
......
92 95
rsMaybeNoData :: (JSON a) => Maybe a -> ResultEntry
93 96
rsMaybeNoData = maybe rsNoData rsNormal
94 97

  
98
-- | Helper to declare a result from a 'ErrorResult' (an error happened
99
-- while retrieving the data from a config, or there was no data).
100
-- This function should be used if an error signals there was no data.
101
rsErrorNoData :: (JSON a) => ErrorResult a -> ResultEntry
102
rsErrorNoData res = case res of
103
  Ok  x -> rsNormal x
104
  Bad _ -> rsNoData
105

  
95 106
-- | Helper to declare a result from a 'Maybe'. This version returns
96 107
-- a 'RSUnavail' in case of 'Nothing'. It should be used for optional
97 108
-- fields that are not set. For cases where 'Nothing' means that there
b/src/Ganeti/Query/Instance.hs
39 39
import Ganeti.BasicTypes
40 40
import Ganeti.Common
41 41
import Ganeti.Config
42
import Ganeti.Errors
42 43
import Ganeti.Objects
43 44
import Ganeti.Query.Common
44 45
import Ganeti.Query.Language
......
64 65
instanceFields :: FieldList Instance Runtime
65 66
instanceFields =
66 67
  -- Simple fields
67
  [ (FieldDefinition "disk_template" "Disk_template" QFTText
68
  [ (FieldDefinition "admin_state" "InstanceState" QFTText
69
     "Desired state of instance",
70
     FieldSimple (rsNormal . adminStateToRaw . instAdminState), QffNormal)
71
  , (FieldDefinition "admin_up" "Autostart" QFTBool
72
     "Desired state of instance",
73
     FieldSimple (rsNormal . (== AdminUp) . instAdminState), QffNormal)
74
  , (FieldDefinition "disk_template" "Disk_template" QFTText
68 75
     "Instance disk template",
69 76
     FieldSimple (rsNormal . instDiskTemplate), QffNormal)
77
  , (FieldDefinition "disks_active" "DisksActive" QFTBool
78
     "Desired state of instance disks",
79
     FieldSimple (rsNormal . instDisksActive), QffNormal)
70 80
  , (FieldDefinition "name" "Instance" QFTText
71 81
     "Instance name",
72 82
     FieldSimple (rsNormal . instName), QffHostname)
......
79 89
  , (FieldDefinition "os" "OS" QFTText
80 90
     "Operating system",
81 91
     FieldSimple (rsNormal . instOs), QffNormal)
92
  , (FieldDefinition "pnode" "Primary_node" QFTText
93
     "Primary node",
94
     FieldConfig getPrimaryNodeName, QffHostname)
95
  , (FieldDefinition "pnode.group" "PrimaryNodeGroup" QFTText
96
     "Primary node's group",
97
     FieldConfig getPrimaryNodeGroup, QffNormal)
98
  , (FieldDefinition "snodes" "Secondary_Nodes" QFTOther
99
     "Secondary nodes; usually this will just be one node",
100
     FieldConfig (getSecondaryNodeAttribute nodeName), QffNormal)
101
  , (FieldDefinition "snodes.group" "SecondaryNodesGroups" QFTOther
102
     "Node groups of secondary nodes",
103
     FieldConfig (getSecondaryNodeGroupAttribute groupName), QffNormal)
104
  , (FieldDefinition "snodes.group.uuid" "SecondaryNodesGroupsUUID" QFTOther
105
     "Node group UUIDs of secondary nodes",
106
     FieldConfig (getSecondaryNodeGroupAttribute groupUuid), QffNormal)
82 107
  ] ++
83 108

  
84 109
  -- Live fields using special getters
......
98 123
  uuidFields "Instance" ++
99 124
  tagsFields
100 125

  
126
-- * Helper functions for node property retrieval
127

  
128
-- | Helper function for primary node retrieval
129
getPrimaryNode :: ConfigData -> Instance -> ErrorResult Node
130
getPrimaryNode cfg = getInstPrimaryNode cfg . instName
131

  
132
-- | Get primary node hostname
133
getPrimaryNodeName :: ConfigData -> Instance -> ResultEntry
134
getPrimaryNodeName cfg inst =
135
  rsErrorNoData $ (J.showJSON . nodeName) <$> getPrimaryNode cfg inst
136

  
137
-- | Get primary node hostname
138
getPrimaryNodeGroup :: ConfigData -> Instance -> ResultEntry
139
getPrimaryNodeGroup cfg inst =
140
  rsErrorNoData $ (J.showJSON . groupName) <$>
141
    (getPrimaryNode cfg inst >>=
142
    maybeToError "Configuration missing" . getGroupOfNode cfg)
143

  
144
-- | Get secondary nodes - the configuration objects themselves
145
getSecondaryNodes :: ConfigData -> Instance -> ErrorResult [Node]
146
getSecondaryNodes cfg inst = do
147
  pNode <- getPrimaryNode cfg inst
148
  allNodes <- getInstAllNodes cfg $ instName inst
149
  return $ delete pNode allNodes
150

  
151
-- | Get attributes of the secondary nodes
152
getSecondaryNodeAttribute :: (J.JSON a)
153
                          => (Node -> a)
154
                          -> ConfigData
155
                          -> Instance
156
                          -> ResultEntry
157
getSecondaryNodeAttribute getter cfg inst =
158
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodes cfg inst
159

  
160
-- | Get secondary node groups
161
getSecondaryNodeGroups :: ConfigData -> Instance -> ErrorResult [NodeGroup]
162
getSecondaryNodeGroups cfg inst = do
163
  sNodes <- getSecondaryNodes cfg inst
164
  return . catMaybes $ map (getGroupOfNode cfg) sNodes
165

  
166
-- | Get attributes of secondary node groups
167
getSecondaryNodeGroupAttribute :: (J.JSON a)
168
                               => (NodeGroup -> a)
169
                               -> ConfigData
170
                               -> Instance
171
                               -> ResultEntry
172
getSecondaryNodeGroupAttribute getter cfg inst =
173
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodeGroups cfg inst
174

  
101 175
-- * Live fields functionality
102 176

  
103 177
-- | List of node live fields.
......
128 202

  
129 203
-- | Builder for node live fields.
130 204
instanceLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc)
131
                     -> FieldData Instance Runtime
205
                         -> FieldData Instance Runtime
132 206
instanceLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
133 207
  ( FieldDefinition fname ftitle ftype fdoc
134 208
  , FieldRuntime $ instanceLiveRpcCall fname
135 209
  , QffNormal)
136 210

  
137

  
138
-- Functionality related to status and operational status extraction
211
-- * Functionality related to status and operational status extraction
139 212

  
140 213
-- | The documentation text for the instance status field
141 214
statusDocText :: String
142 215
statusDocText =
143 216
  let si = show . instanceStatusToRaw :: InstanceStatus -> String
144
  in "Instance status; " ++
145
     si Running ++
146
     " if instance is set to be running and actually is, " ++
147
     si StatusDown ++
148
     " if instance is stopped and is not running, " ++
149
     si WrongNode ++
150
     " if instance running, but not on its designated primary node, " ++
151
     si ErrorUp ++
152
     " if instance should be stopped, but is actually running, " ++
153
     si ErrorDown ++
154
     " if instance should run, but doesn't, " ++
155
     si NodeDown ++
156
     " if instance's primary node is down, " ++
157
     si NodeOffline ++
158
     " if instance's primary node is marked offline, " ++
159
     si StatusOffline ++
160
     " if instance is offline and does not use dynamic resources"
217
  in  "Instance status; " ++
218
      si Running ++
219
      " if instance is set to be running and actually is, " ++
220
      si StatusDown ++
221
      " if instance is stopped and is not running, " ++
222
      si WrongNode ++
223
      " if instance running, but not on its designated primary node, " ++
224
      si ErrorUp ++
225
      " if instance should be stopped, but is actually running, " ++
226
      si ErrorDown ++
227
      " if instance should run, but doesn't, " ++
228
      si NodeDown ++
229
      " if instance's primary node is down, " ++
230
      si NodeOffline ++
231
      " if instance's primary node is marked offline, " ++
232
      si StatusOffline ++
233
      " if instance is offline and does not use dynamic resources"
161 234

  
162 235
-- | Checks if the primary node of an instance is offline
163 236
isPrimaryOffline :: ConfigData -> Instance -> Bool
164 237
isPrimaryOffline cfg inst =
165
  let pNode = optimisticUnwrapper . getNode cfg $ instPrimaryNode inst
166
  in nodeOffline pNode
238
  let pNodeResult = getNode cfg $ instPrimaryNode inst
239
  in case pNodeResult of
240
     Ok pNode -> nodeOffline pNode
241
     Bad    _ -> error "Programmer error - result assumed to be OK is Bad!"
167 242

  
168 243
-- | Determines the status of a live instance
169 244
liveInstanceStatus :: LiveInfo -> Instance -> InstanceStatus
......
182 257
    AdminOffline -> StatusOffline
183 258

  
184 259
-- | Determines the status of the instance, depending on whether it is possible
185
-- | to communicate with its primary node, on which node it is, and its
186
-- | configuration.
187
determineInstanceStatus :: ConfigData -- ^ The configuration data
188
                        -> Runtime    -- ^ All the data from the live call
189
                        -> Instance   -- ^ The static instance configuration
190
                        -> InstanceStatus -- ^ Result
191
determineInstanceStatus cfg res inst =
192
  if isPrimaryOffline cfg inst
193
    then NodeOffline
194
    else case res of
195
      Left _                -> NodeDown
196
      Right (Just liveData) -> liveInstanceStatus liveData inst
197
      Right Nothing         -> deadInstanceStatus inst
198

  
199
-- | Extracts the status, doing necessary transformations but once
260
-- to communicate with its primary node, on which node it is, and its
261
-- configuration.
262
determineInstanceStatus :: ConfigData      -- ^ The configuration data
263
                        -> Runtime         -- ^ All the data from the live call
264
                        -> Instance        -- ^ Static instance configuration
265
                        -> InstanceStatus  -- ^ Result
266
determineInstanceStatus cfg res inst
267
  | isPrimaryOffline cfg inst = NodeOffline
268
  | otherwise = case res of
269
                  Left _                -> NodeDown
270
                  Right (Just liveData) -> liveInstanceStatus liveData inst
271
                  Right Nothing         -> deadInstanceStatus inst
272

  
273
-- | Extracts the instance status, retrieving it using the functions above and
274
-- transforming it into a 'ResultEntry'.
200 275
statusExtract :: ConfigData -> Runtime -> Instance -> ResultEntry
201 276
statusExtract cfg res inst =
202 277
  rsNormal . J.showJSON . instanceStatusToRaw $
203 278
    determineInstanceStatus cfg res inst
204 279

  
205
-- | Extracts the operational status
280
-- | Extracts the operational status of the instance.
206 281
operStatusExtract :: Runtime -> Instance -> ResultEntry
207 282
operStatusExtract res _ =
208
  rsMaybeNoData $ J.showJSON <$> case res of
209
    Left _  -> Nothing
210
    Right x -> Just $ isJust x
211

  
283
  rsMaybeNoData $ J.showJSON <$>
284
    case res of
285
      Left  _ -> Nothing
286
      Right x -> Just $ isJust x
212 287

  
213
-- Helper functions extracting information as necessary for the generic query
288
-- * Helper functions extracting information as necessary for the generic query
214 289
-- interfaces
215 290

  
216
-- | A function removing the GenericResult wrapper from assuredly OK values
217
optimisticUnwrapper :: GenericResult a b -> b
218
optimisticUnwrapper (Ok x) = x
219
optimisticUnwrapper (Bad _) = error "Programmer error: assumptions are wrong!"
220

  
221
-- | Simple filter of OK results only
222
okNodesOnly :: [GenericResult a Node] -> [Node]
223
okNodesOnly = map optimisticUnwrapper . filter isOk
224

  
225 291
-- | Finds information about the instance in the info delivered by a node
226 292
findInstanceInfo :: Instance
227 293
                 -> ERpcError RpcResultAllInstancesInfo
......
286 352
                            RpcResultError $ "Live data disabled"
287 353
  | otherwise = do
288 354
      let hvSpec = getDefaultHypervisorSpec cfg
289
          instance_nodes = nub . okNodesOnly $
355
          instance_nodes = nub . justOk $
290 356
                             map (getNode cfg . instPrimaryNode) instances
291 357
          good_nodes = nodesWithValidConfig cfg instance_nodes
292 358
      rpcres <- executeRpcCall good_nodes $ RpcCallAllInstancesInfo [hvSpec]

Also available in: Unified diff