Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Query / Instance.hs @ 4e6f1cde

History | View | Annotate | Download (16.3 kB)

1
{-| Implementation of the Ganeti Query2 instance queries.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2013 Google Inc.
8

    
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.
13

    
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.
18

    
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
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Ganeti.Query.Instance
27
  ( Runtime
28
  , fieldsMap
29
  , collectLiveData
30
  ) where
31

    
32
import Control.Applicative
33
import Data.List
34
import Data.Maybe
35
import Data.Monoid
36
import qualified Data.Map as Map
37
import qualified Text.JSON as J
38

    
39
import Ganeti.BasicTypes
40
import Ganeti.Common
41
import Ganeti.Config
42
import qualified Ganeti.Constants as C
43
import qualified Ganeti.ConstantUtils as C
44
import Ganeti.Errors
45
import Ganeti.JSON
46
import Ganeti.Objects
47
import Ganeti.Query.Common
48
import Ganeti.Query.Language
49
import Ganeti.Query.Types
50
import Ganeti.Rpc
51
import Ganeti.Storage.Utils
52
import Ganeti.Types
53

    
54
-- | The LiveInfo structure packs additional information beside the
55
-- 'InstanceInfo'. We also need to know whether the instance information was
56
-- found on the primary node, and encode this as a Bool.
57
type LiveInfo = (InstanceInfo, Bool)
58

    
59
-- | Runtime possibly containing the 'LiveInfo'. See the genericQuery function
60
-- in the Query.hs file for an explanation of the terms used.
61
type Runtime = Either RpcError (Maybe LiveInfo)
62

    
63
-- | The instance fields map.
64
fieldsMap :: FieldMap Instance Runtime
65
fieldsMap = Map.fromList [(fdefName f, v) | v@(f, _, _) <- instanceFields]
66

    
67
-- | The instance fields
68
instanceFields :: FieldList Instance Runtime
69
instanceFields =
70
  -- Simple fields
71
  [ (FieldDefinition "admin_state" "InstanceState" QFTText
72
     "Desired state of instance",
73
     FieldSimple (rsNormal . adminStateToRaw . instAdminState), QffNormal)
74
  , (FieldDefinition "admin_up" "Autostart" QFTBool
75
     "Desired state of instance",
76
     FieldSimple (rsNormal . (== AdminUp) . instAdminState), QffNormal)
77
  , (FieldDefinition "disk_template" "Disk_template" QFTText
78
     "Instance disk template",
79
     FieldSimple (rsNormal . instDiskTemplate), QffNormal)
80
  , (FieldDefinition "disks_active" "DisksActive" QFTBool
81
     "Desired state of instance disks",
82
     FieldSimple (rsNormal . instDisksActive), QffNormal)
83
  , (FieldDefinition "name" "Instance" QFTText
84
     "Instance name",
85
     FieldSimple (rsNormal . instName), QffHostname)
86
  , (FieldDefinition "hypervisor" "Hypervisor" QFTText
87
     "Hypervisor name",
88
     FieldSimple (rsNormal . instHypervisor), QffNormal)
89
  , (FieldDefinition "network_port" "Network_port" QFTOther
90
     "Instance network port if available (e.g. for VNC console)",
91
     FieldSimple (rsMaybeUnavail . instNetworkPort), QffNormal)
92
  , (FieldDefinition "os" "OS" QFTText
93
     "Operating system",
94
     FieldSimple (rsNormal . instOs), QffNormal)
95
  , (FieldDefinition "pnode" "Primary_node" QFTText
96
     "Primary node",
97
     FieldConfig getPrimaryNodeName, QffHostname)
98
  , (FieldDefinition "pnode.group" "PrimaryNodeGroup" QFTText
99
     "Primary node's group",
100
     FieldConfig getPrimaryNodeGroup, QffNormal)
101
  , (FieldDefinition "snodes" "Secondary_Nodes" QFTOther
102
     "Secondary nodes; usually this will just be one node",
103
     FieldConfig (getSecondaryNodeAttribute nodeName), QffNormal)
104
  , (FieldDefinition "snodes.group" "SecondaryNodesGroups" QFTOther
105
     "Node groups of secondary nodes",
106
     FieldConfig (getSecondaryNodeGroupAttribute groupName), QffNormal)
107
  , (FieldDefinition "snodes.group.uuid" "SecondaryNodesGroupsUUID" QFTOther
108
     "Node group UUIDs of secondary nodes",
109
     FieldConfig (getSecondaryNodeGroupAttribute groupUuid), QffNormal)
110
  ] ++
111

    
112
  -- Instance parameter fields, whole
113
  [ (FieldDefinition "hvparams" "HypervisorParameters" QFTOther
114
     "Hypervisor parameters (merged)",
115
     FieldConfig ((rsNormal .) . getFilledInstHvParams), QffNormal)
116
  , (FieldDefinition "beparams" "BackendParameters" QFTOther
117
     "Backend parameters (merged)",
118
     FieldConfig ((rsErrorNoData .) . getFilledInstBeParams), QffNormal)
119
  , (FieldDefinition "osparams" "OpSysParameters" QFTOther
120
     "Operating system parameters (merged)",
121
     FieldConfig ((rsNormal .) . getFilledInstOsParams), QffNormal)
122
  , (FieldDefinition "custom_hvparams" "CustomHypervisorParameters" QFTOther
123
     "Custom hypervisor parameters",
124
     FieldSimple (rsNormal . instHvparams), QffNormal)
125
  , (FieldDefinition "custom_beparams" "CustomBackendParameters" QFTOther
126
     "Custom backend parameters",
127
     FieldSimple (rsNormal . instBeparams), QffNormal)
128
  , (FieldDefinition "custom_osparams" "CustomOpSysParameters" QFTOther
129
     "Custom operating system parameters",
130
     FieldSimple (rsNormal . instOsparams), QffNormal)
131
  ] ++
132

    
133
  -- Instance parameter fields, generated
134
  map (buildBeParamField beParamGetter) allBeParamFields ++
135
  map (buildHvParamField hvParamGetter) (C.toList C.hvsParameters) ++
136

    
137
  -- Live fields using special getters
138
  [ (FieldDefinition "status" "Status" QFTText
139
     statusDocText,
140
     FieldConfigRuntime statusExtract, QffNormal)
141
  , (FieldDefinition "oper_state" "Running" QFTBool
142
     "Actual state of instance",
143
     FieldRuntime operStatusExtract, QffNormal)
144
  ] ++
145

    
146
  -- Simple live fields
147
  map instanceLiveFieldBuilder instanceLiveFieldsDefs ++
148

    
149
  -- Generated fields
150
  serialFields "Instance" ++
151
  uuidFields "Instance" ++
152
  tagsFields
153

    
154
-- * Helper functions for node property retrieval
155

    
156
-- | Helper function for primary node retrieval
157
getPrimaryNode :: ConfigData -> Instance -> ErrorResult Node
158
getPrimaryNode cfg = getInstPrimaryNode cfg . instName
159

    
160
-- | Get primary node hostname
161
getPrimaryNodeName :: ConfigData -> Instance -> ResultEntry
162
getPrimaryNodeName cfg inst =
163
  rsErrorNoData $ nodeName <$> getPrimaryNode cfg inst
164

    
165
-- | Get primary node hostname
166
getPrimaryNodeGroup :: ConfigData -> Instance -> ResultEntry
167
getPrimaryNodeGroup cfg inst =
168
  rsErrorNoData $ (J.showJSON . groupName) <$>
169
    (getPrimaryNode cfg inst >>=
170
    maybeToError "Configuration missing" . getGroupOfNode cfg)
171

    
172
-- | Get secondary nodes - the configuration objects themselves
173
getSecondaryNodes :: ConfigData -> Instance -> ErrorResult [Node]
174
getSecondaryNodes cfg inst = do
175
  pNode <- getPrimaryNode cfg inst
176
  allNodes <- getInstAllNodes cfg $ instName inst
177
  return $ delete pNode allNodes
178

    
179
-- | Get attributes of the secondary nodes
180
getSecondaryNodeAttribute :: (J.JSON a)
181
                          => (Node -> a)
182
                          -> ConfigData
183
                          -> Instance
184
                          -> ResultEntry
185
getSecondaryNodeAttribute getter cfg inst =
186
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodes cfg inst
187

    
188
-- | Get secondary node groups
189
getSecondaryNodeGroups :: ConfigData -> Instance -> ErrorResult [NodeGroup]
190
getSecondaryNodeGroups cfg inst = do
191
  sNodes <- getSecondaryNodes cfg inst
192
  return . catMaybes $ map (getGroupOfNode cfg) sNodes
193

    
194
-- | Get attributes of secondary node groups
195
getSecondaryNodeGroupAttribute :: (J.JSON a)
196
                               => (NodeGroup -> a)
197
                               -> ConfigData
198
                               -> Instance
199
                               -> ResultEntry
200
getSecondaryNodeGroupAttribute getter cfg inst =
201
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodeGroups cfg inst
202

    
203
-- | Beparam getter builder: given a field, it returns a FieldConfig
204
-- getter, that is a function that takes the config and the object and
205
-- returns the Beparam field specified when the getter was built.
206
beParamGetter :: String       -- ^ The field we are building the getter for
207
              -> ConfigData   -- ^ The configuration object
208
              -> Instance     -- ^ The instance configuration object
209
              -> ResultEntry  -- ^ The result
210
beParamGetter field config inst =
211
  case getFilledInstBeParams config inst of
212
    Ok beParams -> dictFieldGetter field $ Just beParams
213
    Bad       _ -> rsNoData
214

    
215
-- | Hvparam getter builder: given a field, it returns a FieldConfig
216
-- getter, that is a function that takes the config and the object and
217
-- returns the Hvparam field specified when the getter was built.
218
hvParamGetter :: String -- ^ The field we're building the getter for
219
              -> ConfigData -> Instance -> ResultEntry
220
hvParamGetter field cfg inst =
221
  rsMaybeUnavail . Map.lookup field . fromContainer $
222
                                        getFilledInstHvParams cfg inst
223

    
224
-- * Live fields functionality
225

    
226
-- | List of node live fields.
227
instanceLiveFieldsDefs :: [(FieldName, FieldTitle, FieldType, String, FieldDoc)]
228
instanceLiveFieldsDefs =
229
  [ ("oper_ram", "Memory", QFTUnit, "oper_ram",
230
     "Actual memory usage as seen by hypervisor")
231
  , ("oper_vcpus", "VCPUs", QFTNumber, "oper_vcpus",
232
     "Actual number of VCPUs as seen by hypervisor")
233
  ]
234

    
235
-- | Map each name to a function that extracts that value from the RPC result.
236
instanceLiveFieldExtract :: FieldName -> InstanceInfo -> Instance -> J.JSValue
237
instanceLiveFieldExtract "oper_ram"   info _ = J.showJSON $ instInfoMemory info
238
instanceLiveFieldExtract "oper_vcpus" info _ = J.showJSON $ instInfoVcpus info
239
instanceLiveFieldExtract n _ _ = J.showJSON $
240
  "The field " ++ n ++ " is not an expected or extractable live field!"
241

    
242
-- | Helper for extracting field from RPC result.
243
instanceLiveRpcCall :: FieldName -> Runtime -> Instance -> ResultEntry
244
instanceLiveRpcCall fname (Right (Just (res, _))) inst =
245
  case instanceLiveFieldExtract fname res inst of
246
    J.JSNull -> rsNoData
247
    x        -> rsNormal x
248
instanceLiveRpcCall _ (Right Nothing) _ = rsUnavail
249
instanceLiveRpcCall _ (Left err) _ =
250
  ResultEntry (rpcErrorToStatus err) Nothing
251

    
252
-- | Builder for node live fields.
253
instanceLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc)
254
                         -> FieldData Instance Runtime
255
instanceLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
256
  ( FieldDefinition fname ftitle ftype fdoc
257
  , FieldRuntime $ instanceLiveRpcCall fname
258
  , QffNormal)
259

    
260
-- * Functionality related to status and operational status extraction
261

    
262
-- | The documentation text for the instance status field
263
statusDocText :: String
264
statusDocText =
265
  let si = show . instanceStatusToRaw :: InstanceStatus -> String
266
  in  "Instance status; " ++
267
      si Running ++
268
      " if instance is set to be running and actually is, " ++
269
      si StatusDown ++
270
      " if instance is stopped and is not running, " ++
271
      si WrongNode ++
272
      " if instance running, but not on its designated primary node, " ++
273
      si ErrorUp ++
274
      " if instance should be stopped, but is actually running, " ++
275
      si ErrorDown ++
276
      " if instance should run, but doesn't, " ++
277
      si NodeDown ++
278
      " if instance's primary node is down, " ++
279
      si NodeOffline ++
280
      " if instance's primary node is marked offline, " ++
281
      si StatusOffline ++
282
      " if instance is offline and does not use dynamic resources"
283

    
284
-- | Checks if the primary node of an instance is offline
285
isPrimaryOffline :: ConfigData -> Instance -> Bool
286
isPrimaryOffline cfg inst =
287
  let pNodeResult = getNode cfg $ instPrimaryNode inst
288
  in case pNodeResult of
289
     Ok pNode -> nodeOffline pNode
290
     Bad    _ -> error "Programmer error - result assumed to be OK is Bad!"
291

    
292
-- | Determines the status of a live instance
293
liveInstanceStatus :: LiveInfo -> Instance -> InstanceStatus
294
liveInstanceStatus (_, foundOnPrimary) inst
295
  | not foundOnPrimary    = WrongNode
296
  | adminState == AdminUp = Running
297
  | otherwise             = ErrorUp
298
  where adminState = instAdminState inst
299

    
300
-- | Determines the status of a dead instance.
301
deadInstanceStatus :: Instance -> InstanceStatus
302
deadInstanceStatus inst =
303
  case instAdminState inst of
304
    AdminUp      -> ErrorDown
305
    AdminDown    -> StatusDown
306
    AdminOffline -> StatusOffline
307

    
308
-- | Determines the status of the instance, depending on whether it is possible
309
-- to communicate with its primary node, on which node it is, and its
310
-- configuration.
311
determineInstanceStatus :: ConfigData      -- ^ The configuration data
312
                        -> Runtime         -- ^ All the data from the live call
313
                        -> Instance        -- ^ Static instance configuration
314
                        -> InstanceStatus  -- ^ Result
315
determineInstanceStatus cfg res inst
316
  | isPrimaryOffline cfg inst = NodeOffline
317
  | otherwise = case res of
318
                  Left _                -> NodeDown
319
                  Right (Just liveData) -> liveInstanceStatus liveData inst
320
                  Right Nothing         -> deadInstanceStatus inst
321

    
322
-- | Extracts the instance status, retrieving it using the functions above and
323
-- transforming it into a 'ResultEntry'.
324
statusExtract :: ConfigData -> Runtime -> Instance -> ResultEntry
325
statusExtract cfg res inst =
326
  rsNormal . J.showJSON . instanceStatusToRaw $
327
    determineInstanceStatus cfg res inst
328

    
329
-- | Extracts the operational status of the instance.
330
operStatusExtract :: Runtime -> Instance -> ResultEntry
331
operStatusExtract res _ =
332
  rsMaybeNoData $ J.showJSON <$>
333
    case res of
334
      Left  _ -> Nothing
335
      Right x -> Just $ isJust x
336

    
337
-- * Helper functions extracting information as necessary for the generic query
338
-- interfaces
339

    
340
-- | Finds information about the instance in the info delivered by a node
341
findInstanceInfo :: Instance
342
                 -> ERpcError RpcResultAllInstancesInfo
343
                 -> Maybe InstanceInfo
344
findInstanceInfo inst nodeResponse =
345
  case nodeResponse of
346
    Left  _err    -> Nothing
347
    Right allInfo ->
348
      let instances = rpcResAllInstInfoInstances allInfo
349
          maybeMatch = pickPairUnique (instName inst) instances
350
      in snd <$> maybeMatch
351

    
352
-- | Finds the node information ('RPCResultError') or the instance information
353
-- (Maybe 'LiveInfo').
354
extractLiveInfo :: [(Node, ERpcError RpcResultAllInstancesInfo)]
355
                -> Instance
356
                -> Runtime
357
extractLiveInfo nodeResultList inst =
358
  let uuidResultList = [(nodeUuid x, y) | (x, y) <- nodeResultList]
359
      pNodeUuid = instPrimaryNode inst
360
      maybeRPCError = getNodeStatus uuidResultList pNodeUuid
361
  in case maybeRPCError of
362
       Just err -> Left err
363
       Nothing  -> Right $ getInstanceStatus uuidResultList pNodeUuid inst
364

    
365
-- | Tries to find out if the node given by the uuid is bad - unreachable or
366
-- returning errors, does not mather for the purpose of this call.
367
getNodeStatus :: [(String, ERpcError RpcResultAllInstancesInfo)]
368
              -> String
369
              -> Maybe RpcError
370
getNodeStatus uuidList uuid =
371
  case snd <$> pickPairUnique uuid uuidList of
372
    Just (Left err) -> Just err
373
    Just (Right _)  -> Nothing
374
    Nothing         -> Just . RpcResultError $
375
                         "Primary node response not present"
376

    
377
-- | Retrieves the instance information if it is present anywhere in the all
378
-- instances RPC result. Notes if it originates from the primary node.
379
-- All nodes are represented as UUID's for ease of use.
380
getInstanceStatus :: [(String, ERpcError RpcResultAllInstancesInfo)]
381
                  -> String
382
                  -> Instance
383
                  -> Maybe LiveInfo
384
getInstanceStatus uuidList pNodeUuid inst =
385
  let primarySearchResult =
386
        snd <$> pickPairUnique pNodeUuid uuidList >>= findInstanceInfo inst
387
  in case primarySearchResult of
388
       Just instInfo -> Just (instInfo, True)
389
       Nothing       ->
390
         let allSearchResult =
391
               getFirst . mconcat $ map
392
               (First . findInstanceInfo inst . snd) uuidList
393
         in case allSearchResult of
394
              Just liveInfo -> Just (liveInfo, False)
395
              Nothing       -> Nothing
396

    
397
-- | Collect live data from RPC query if enabled.
398
collectLiveData :: Bool -> ConfigData -> [Instance] -> IO [(Instance, Runtime)]
399
collectLiveData liveDataEnabled cfg instances
400
  | not liveDataEnabled = return . zip instances . repeat . Left .
401
                            RpcResultError $ "Live data disabled"
402
  | otherwise = do
403
      let hvSpec = getDefaultHypervisorSpec cfg
404
          instance_nodes = nub . justOk $
405
                             map (getNode cfg . instPrimaryNode) instances
406
          good_nodes = nodesWithValidConfig cfg instance_nodes
407
      rpcres <- executeRpcCall good_nodes $ RpcCallAllInstancesInfo [hvSpec]
408
      return . zip instances . map (extractLiveInfo rpcres) $ instances