Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Query / Instance.hs @ b9666288

History | View | Annotate | Download (33 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
  , instanceFields
31
  , instanceAliases
32
  ) where
33

    
34
import Control.Applicative
35
import Data.Either
36
import Data.List
37
import Data.Maybe
38
import Data.Monoid
39
import qualified Data.Map as Map
40
import Data.Ord (comparing)
41
import qualified Text.JSON as J
42
import Text.Printf
43

    
44
import Ganeti.BasicTypes
45
import Ganeti.Common
46
import Ganeti.Config
47
import qualified Ganeti.Constants as C
48
import qualified Ganeti.ConstantUtils as C
49
import Ganeti.Errors
50
import Ganeti.JSON
51
import Ganeti.Objects
52
import Ganeti.Query.Common
53
import Ganeti.Query.Language
54
import Ganeti.Query.Types
55
import Ganeti.Rpc
56
import Ganeti.Storage.Utils
57
import Ganeti.Types
58
import Ganeti.Utils (formatOrdinal)
59

    
60
-- | The LiveInfo consists of two entries whose presence is independent.
61
-- The 'InstanceInfo' is the live instance information, accompanied by a bool
62
-- signifying if it was found on its designated primary node or not.
63
-- The 'InstanceConsoleInfo' describes how to connect to an instance.
64
-- Any combination of these may or may not be present, depending on node and
65
-- instance availability.
66
type LiveInfo = (Maybe (InstanceInfo, Bool), Maybe InstanceConsoleInfo)
67

    
68
-- | Runtime containing the 'LiveInfo'. See the genericQuery function in
69
-- the Query.hs file for an explanation of the terms used.
70
type Runtime = Either RpcError LiveInfo
71

    
72
-- | The instance fields map.
73
fieldsMap :: FieldMap Instance Runtime
74
fieldsMap = Map.fromList [(fdefName f, v) | v@(f, _, _) <- aliasedFields]
75

    
76
-- | The instance aliases.
77
instanceAliases :: [(FieldName, FieldName)]
78
instanceAliases =
79
  [ ("vcpus", "be/vcpus")
80
  , ("be/memory", "be/maxmem")
81
  , ("sda_size", "disk.size/0")
82
  , ("sdb_size", "disk.size/1")
83
  , ("ip", "nic.ip/0")
84
  , ("mac", "nic.mac/0")
85
  , ("bridge", "nic.bridge/0")
86
  , ("nic_mode", "nic.mode/0")
87
  , ("nic_link", "nic.link/0")
88
  , ("nic_network", "nic.network/0")
89
  ]
90

    
91
-- | The aliased instance fields.
92
aliasedFields :: FieldList Instance Runtime
93
aliasedFields = aliasFields instanceAliases instanceFields
94

    
95
-- | The instance fields.
96
instanceFields :: FieldList Instance Runtime
97
instanceFields =
98
  -- Simple fields
99
  [ (FieldDefinition "admin_state" "InstanceState" QFTText
100
     "Desired state of instance",
101
     FieldSimple (rsNormal . adminStateToRaw . instAdminState), QffNormal)
102
  , (FieldDefinition "admin_up" "Autostart" QFTBool
103
     "Desired state of instance",
104
     FieldSimple (rsNormal . (== AdminUp) . instAdminState), QffNormal)
105
  , (FieldDefinition "disk_template" "Disk_template" QFTText
106
     "Instance disk template",
107
     FieldSimple (rsNormal . instDiskTemplate), QffNormal)
108
  , (FieldDefinition "disks_active" "DisksActive" QFTBool
109
     "Desired state of instance disks",
110
     FieldSimple (rsNormal . instDisksActive), QffNormal)
111
  , (FieldDefinition "name" "Instance" QFTText
112
     "Instance name",
113
     FieldSimple (rsNormal . instName), QffHostname)
114
  , (FieldDefinition "hypervisor" "Hypervisor" QFTText
115
     "Hypervisor name",
116
     FieldSimple (rsNormal . instHypervisor), QffNormal)
117
  , (FieldDefinition "network_port" "Network_port" QFTOther
118
     "Instance network port if available (e.g. for VNC console)",
119
     FieldSimple (rsMaybeUnavail . instNetworkPort), QffNormal)
120
  , (FieldDefinition "os" "OS" QFTText
121
     "Operating system",
122
     FieldSimple (rsNormal . instOs), QffNormal)
123
  , (FieldDefinition "pnode" "Primary_node" QFTText
124
     "Primary node",
125
     FieldConfig getPrimaryNodeName, QffHostname)
126
  , (FieldDefinition "pnode.group" "PrimaryNodeGroup" QFTText
127
     "Primary node's group",
128
     FieldConfig getPrimaryNodeGroupName, QffNormal)
129
  , (FieldDefinition "pnode.group.uuid" "PrimaryNodeGroupUUID" QFTText
130
     "Primary node's group UUID",
131
     FieldConfig getPrimaryNodeGroupUuid, QffNormal)
132
  , (FieldDefinition "snodes" "Secondary_Nodes" QFTOther
133
     "Secondary nodes; usually this will just be one node",
134
     FieldConfig (getSecondaryNodeAttribute nodeName), QffNormal)
135
  , (FieldDefinition "snodes.group" "SecondaryNodesGroups" QFTOther
136
     "Node groups of secondary nodes",
137
     FieldConfig (getSecondaryNodeGroupAttribute groupName), QffNormal)
138
  , (FieldDefinition "snodes.group.uuid" "SecondaryNodesGroupsUUID" QFTOther
139
     "Node group UUIDs of secondary nodes",
140
     FieldConfig (getSecondaryNodeGroupAttribute groupUuid), QffNormal)
141
  ] ++
142

    
143
  -- Instance parameter fields, whole
144
  [ (FieldDefinition "hvparams" "HypervisorParameters" QFTOther
145
     "Hypervisor parameters (merged)",
146
     FieldConfig
147
       ((rsNormal .) . getFilledInstHvParams (C.toList C.hvcGlobals)),
148
     QffNormal),
149

    
150
    (FieldDefinition "beparams" "BackendParameters" QFTOther
151
     "Backend parameters (merged)",
152
     FieldConfig ((rsErrorNoData .) . getFilledInstBeParams), QffNormal)
153
  , (FieldDefinition "osparams" "OpSysParameters" QFTOther
154
     "Operating system parameters (merged)",
155
     FieldConfig ((rsNormal .) . getFilledInstOsParams), QffNormal)
156
  , (FieldDefinition "custom_hvparams" "CustomHypervisorParameters" QFTOther
157
     "Custom hypervisor parameters",
158
     FieldSimple (rsNormal . instHvparams), QffNormal)
159
  , (FieldDefinition "custom_beparams" "CustomBackendParameters" QFTOther
160
     "Custom backend parameters",
161
     FieldSimple (rsNormal . instBeparams), QffNormal)
162
  , (FieldDefinition "custom_osparams" "CustomOpSysParameters" QFTOther
163
     "Custom operating system parameters",
164
     FieldSimple (rsNormal . instOsparams), QffNormal)
165
  , (FieldDefinition "custom_nicparams" "CustomNicParameters" QFTOther
166
     "Custom network interface parameters",
167
     FieldSimple (rsNormal . map nicNicparams . instNics), QffNormal)
168
  ] ++
169

    
170
  -- Instance parameter fields, generated
171
  map (buildBeParamField beParamGetter) allBeParamFields ++
172
  map (buildHvParamField hvParamGetter) (C.toList C.hvsParameters) ++
173

    
174
  -- Aggregate disk parameter fields
175
  [ (FieldDefinition "disk_usage" "DiskUsage" QFTUnit
176
     "Total disk space used by instance on each of its nodes; this is not the\
177
     \ disk size visible to the instance, but the usage on the node",
178
     FieldSimple (rsNormal . getDiskSizeRequirements), QffNormal)
179
  , (FieldDefinition "disk.count" "Disks" QFTNumber
180
     "Number of disks",
181
     FieldSimple (rsNormal . length . instDisks), QffNormal)
182
  , (FieldDefinition "disk.sizes" "Disk_sizes" QFTOther
183
     "List of disk sizes",
184
     FieldSimple (rsNormal . map diskSize . instDisks), QffNormal)
185
  , (FieldDefinition "disk.spindles" "Disk_spindles" QFTOther
186
     "List of disk spindles",
187
     FieldSimple (rsNormal . map (MaybeForJSON . diskSpindles) .
188
                  instDisks),
189
     QffNormal)
190
  , (FieldDefinition "disk.names" "Disk_names" QFTOther
191
     "List of disk names",
192
     FieldSimple (rsNormal . map (MaybeForJSON . diskName) .
193
                  instDisks),
194
     QffNormal)
195
  , (FieldDefinition "disk.uuids" "Disk_UUIDs" QFTOther
196
     "List of disk UUIDs",
197
     FieldSimple (rsNormal . map diskUuid . instDisks), QffNormal)
198
  ] ++
199

    
200
  -- Per-disk parameter fields
201
  instantiateIndexedFields C.maxDisks
202
  [ (fieldDefinitionCompleter "disk.size/%d" "Disk/%d" QFTUnit
203
     "Disk size of %s disk",
204
     getIndexedField instDisks diskSize, QffNormal)
205
  , (fieldDefinitionCompleter "disk.spindles/%d" "DiskSpindles/%d" QFTNumber
206
     "Spindles of %s disk",
207
     getIndexedOptionalField instDisks diskSpindles, QffNormal)
208
  , (fieldDefinitionCompleter "disk.name/%d" "DiskName/%d" QFTText
209
     "Name of %s disk",
210
     getIndexedOptionalField instDisks diskName, QffNormal)
211
  , (fieldDefinitionCompleter "disk.uuid/%d" "DiskUUID/%d" QFTText
212
     "UUID of %s disk",
213
     getIndexedField instDisks diskUuid, QffNormal)
214
  ] ++
215

    
216
  -- Aggregate nic parameter fields
217
  [ (FieldDefinition "nic.count" "NICs" QFTNumber
218
     "Number of network interfaces",
219
     FieldSimple (rsNormal . length . instNics), QffNormal)
220
  , (FieldDefinition "nic.macs" "NIC_MACs" QFTOther
221
     (nicAggDescPrefix ++ "MAC address"),
222
     FieldSimple (rsNormal . map nicMac . instNics), QffNormal)
223
  , (FieldDefinition "nic.ips" "NIC_IPs" QFTOther
224
     (nicAggDescPrefix ++ "IP address"),
225
     FieldSimple (rsNormal . map (MaybeForJSON . nicIp) . instNics),
226
     QffNormal)
227
  , (FieldDefinition "nic.names" "NIC_Names" QFTOther
228
     (nicAggDescPrefix ++ "name"),
229
     FieldSimple (rsNormal . map (MaybeForJSON . nicName) . instNics),
230
     QffNormal)
231
  , (FieldDefinition "nic.uuids" "NIC_UUIDs" QFTOther
232
     (nicAggDescPrefix ++ "UUID"),
233
     FieldSimple (rsNormal . map nicUuid . instNics), QffNormal)
234
  , (FieldDefinition "nic.modes" "NIC_modes" QFTOther
235
     (nicAggDescPrefix ++ "mode"),
236
     FieldConfig (\cfg -> rsNormal . map
237
       (nicpMode . fillNicParamsFromConfig cfg . nicNicparams)
238
       . instNics),
239
     QffNormal)
240
  , (FieldDefinition "nic.bridges" "NIC_bridges" QFTOther
241
     (nicAggDescPrefix ++ "bridge"),
242
     FieldConfig (\cfg -> rsNormal . map (MaybeForJSON . getNicBridge .
243
       fillNicParamsFromConfig cfg . nicNicparams) . instNics),
244
     QffNormal)
245
  , (FieldDefinition "nic.links" "NIC_links" QFTOther
246
     (nicAggDescPrefix ++ "link"),
247
     FieldConfig (\cfg -> rsNormal . map
248
       (nicpLink . fillNicParamsFromConfig cfg . nicNicparams)
249
       . instNics),
250
     QffNormal)
251
  , (FieldDefinition "nic.networks" "NIC_networks" QFTOther
252
     "List containing each interface's network",
253
     FieldSimple (rsNormal . map (MaybeForJSON . nicNetwork) . instNics),
254
     QffNormal)
255
  , (FieldDefinition "nic.networks.names" "NIC_networks_names" QFTOther
256
     "List containing the name of each interface's network",
257
     FieldConfig (\cfg -> rsNormal . map
258
       (\x -> MaybeForJSON (getNetworkName cfg <$> nicNetwork x))
259
       . instNics),
260
     QffNormal)
261
  ] ++
262

    
263
  -- Per-nic parameter fields
264
  instantiateIndexedFields C.maxNics
265
  [ (fieldDefinitionCompleter "nic.ip/%d" "NicIP/%d" QFTText
266
     ("IP address" ++ nicDescSuffix),
267
     getIndexedOptionalField instNics nicIp, QffNormal)
268
  , (fieldDefinitionCompleter "nic.uuid/%d" "NicUUID/%d" QFTText
269
     ("UUID address" ++ nicDescSuffix),
270
     getIndexedField instNics nicUuid, QffNormal)
271
  , (fieldDefinitionCompleter "nic.mac/%d" "NicMAC/%d" QFTText
272
     ("MAC address" ++ nicDescSuffix),
273
     getIndexedField instNics nicMac, QffNormal)
274
  , (fieldDefinitionCompleter "nic.name/%d" "NicName/%d" QFTText
275
     ("Name address" ++ nicDescSuffix),
276
     getIndexedOptionalField instNics nicName, QffNormal)
277
  , (fieldDefinitionCompleter "nic.network/%d" "NicNetwork/%d" QFTText
278
     ("Network" ++ nicDescSuffix),
279
     getIndexedOptionalField instNics nicNetwork, QffNormal)
280
  , (fieldDefinitionCompleter "nic.mode/%d" "NicMode/%d" QFTText
281
     ("Mode" ++ nicDescSuffix),
282
     getIndexedNicField nicpMode, QffNormal)
283
  , (fieldDefinitionCompleter "nic.link/%d" "NicLink/%d" QFTText
284
     ("Link" ++ nicDescSuffix),
285
     getIndexedNicField nicpLink, QffNormal)
286
  , (fieldDefinitionCompleter "nic.network.name/%d" "NicNetworkName/%d" QFTText
287
     ("Network name" ++ nicDescSuffix),
288
     getIndexedNicNetworkNameField, QffNormal)
289
  , (fieldDefinitionCompleter "nic.bridge/%d" "NicBridge/%d" QFTText
290
     ("Bridge" ++ nicDescSuffix),
291
     getOptionalIndexedNicField getNicBridge, QffNormal)
292
  ] ++
293

    
294
  -- Live fields using special getters
295
  [ (FieldDefinition "status" "Status" QFTText
296
     statusDocText,
297
     FieldConfigRuntime statusExtract, QffNormal)
298
  , (FieldDefinition "oper_state" "Running" QFTBool
299
     "Actual state of instance",
300
     FieldRuntime operStatusExtract, QffNormal),
301

    
302
    (FieldDefinition "console" "Console" QFTOther
303
     "Instance console information",
304
     FieldRuntime consoleExtract, QffNormal)
305
  ] ++
306

    
307
  -- Simple live fields
308
  map instanceLiveFieldBuilder instanceLiveFieldsDefs ++
309

    
310
  -- Common fields
311
  timeStampFields ++
312
  serialFields "Instance" ++
313
  uuidFields "Instance" ++
314
  tagsFields
315

    
316
-- * Helper functions for node property retrieval
317

    
318
-- | Constant suffix of network interface field descriptions.
319
nicDescSuffix ::String
320
nicDescSuffix = " of %s network interface"
321

    
322
-- | Almost-constant suffix of aggregate network interface field descriptions.
323
nicAggDescPrefix ::String
324
nicAggDescPrefix = "List containing each network interface's "
325

    
326
-- | Given a network name id, returns the network's name.
327
getNetworkName :: ConfigData -> String -> NonEmptyString
328
getNetworkName cfg = networkName . (Map.!) (fromContainer $ configNetworks cfg)
329

    
330
-- | Gets the bridge of a NIC.
331
getNicBridge :: FilledNicParams -> Maybe String
332
getNicBridge nicParams
333
  | nicpMode nicParams == NMBridged = Just $ nicpLink nicParams
334
  | otherwise                       = Nothing
335

    
336
-- | Fill partial NIC params by using the defaults from the configuration.
337
fillNicParamsFromConfig :: ConfigData -> PartialNicParams -> FilledNicParams
338
fillNicParamsFromConfig cfg = fillNicParams (getDefaultNicParams cfg)
339

    
340
-- | Retrieves the default network interface parameters.
341
getDefaultNicParams :: ConfigData -> FilledNicParams
342
getDefaultNicParams cfg =
343
  (Map.!) (fromContainer . clusterNicparams . configCluster $ cfg) C.ppDefault
344

    
345
-- | Returns a field that retrieves a given NIC's network name.
346
getIndexedNicNetworkNameField :: Int -> FieldGetter Instance Runtime
347
getIndexedNicNetworkNameField index =
348
  FieldConfig (\cfg inst -> rsMaybeUnavail $ do
349
    nicObj <- maybeAt index $ instNics inst
350
    nicNetworkId <- nicNetwork nicObj
351
    return $ getNetworkName cfg nicNetworkId)
352

    
353
-- | Gets a fillable NIC field.
354
getIndexedNicField :: (J.JSON a)
355
                   => (FilledNicParams -> a)
356
                   -> Int
357
                   -> FieldGetter Instance Runtime
358
getIndexedNicField getter =
359
  getOptionalIndexedNicField (\x -> Just . getter $ x)
360

    
361
-- | Gets an optional fillable NIC field.
362
getOptionalIndexedNicField :: (J.JSON a)
363
                           => (FilledNicParams -> Maybe a)
364
                           -> Int
365
                           -> FieldGetter Instance Runtime
366
getOptionalIndexedNicField =
367
  getIndexedFieldWithDefault
368
    (map nicNicparams . instNics) (\x _ -> getDefaultNicParams x) fillNicParams
369

    
370
-- | Creates a function which produces a 'FieldGetter' when fed an index. Works
371
-- for fields that should be filled out through the use of a default.
372
getIndexedFieldWithDefault :: (J.JSON c)
373
  => (Instance -> [a])             -- ^ Extracts a list of incomplete objects
374
  -> (ConfigData -> Instance -> b) -- ^ Extracts the default object
375
  -> (b -> a -> b)                 -- ^ Fills the default object
376
  -> (b -> Maybe c)                -- ^ Extracts an obj property
377
  -> Int                           -- ^ Index in list to use
378
  -> FieldGetter Instance Runtime  -- ^ Result
379
getIndexedFieldWithDefault
380
  listGetter defaultGetter fillFn propertyGetter index =
381
  FieldConfig (\cfg inst -> rsMaybeUnavail $ do
382
                              incompleteObj <- maybeAt index $ listGetter inst
383
                              let defaultObj = defaultGetter cfg inst
384
                                  completeObj = fillFn defaultObj incompleteObj
385
                              propertyGetter completeObj)
386

    
387
-- | Creates a function which produces a 'FieldGetter' when fed an index. Works
388
-- for fields that may not return a value, expressed through the Maybe monad.
389
getIndexedOptionalField :: (J.JSON b)
390
                        => (Instance -> [a]) -- ^ Extracts a list of objects
391
                        -> (a -> Maybe b)    -- ^ Possibly gets a property
392
                                             -- from an object
393
                        -> Int               -- ^ Index in list to use
394
                        -> FieldGetter Instance Runtime -- ^ Result
395
getIndexedOptionalField extractor optPropertyGetter index =
396
  FieldSimple(\inst -> rsMaybeUnavail $ do
397
                         obj <- maybeAt index $ extractor inst
398
                         optPropertyGetter obj)
399

    
400
-- | Creates a function which produces a 'FieldGetter' when fed an index.
401
-- Works only for fields that surely return a value.
402
getIndexedField :: (J.JSON b)
403
                => (Instance -> [a]) -- ^ Extracts a list of objects
404
                -> (a -> b)          -- ^ Gets a property from an object
405
                -> Int               -- ^ Index in list to use
406
                -> FieldGetter Instance Runtime -- ^ Result
407
getIndexedField extractor propertyGetter index =
408
  let optPropertyGetter = Just . propertyGetter
409
  in getIndexedOptionalField extractor optPropertyGetter index
410

    
411
-- | Retrieves a value from an array at an index, using the Maybe monad to
412
-- indicate failure.
413
maybeAt :: Int -> [a] -> Maybe a
414
maybeAt index list
415
  | index >= length list = Nothing
416
  | otherwise            = Just $ list !! index
417

    
418
-- | Primed with format strings for everything but the type, it consumes two
419
-- values and uses them to complete the FieldDefinition.
420
-- Warning: a bit unsafe as it uses printf. Handle with care.
421
fieldDefinitionCompleter :: (PrintfArg t1) => (PrintfArg t2)
422
                         => FieldName
423
                         -> FieldTitle
424
                         -> FieldType
425
                         -> FieldDoc
426
                         -> t1
427
                         -> t2
428
                         -> FieldDefinition
429
fieldDefinitionCompleter fName fTitle fType fDoc firstVal secondVal =
430
  FieldDefinition (printf fName firstVal)
431
                  (printf fTitle firstVal)
432
                  fType
433
                  (printf fDoc secondVal)
434

    
435
-- | Given an incomplete field definition and values that can complete it,
436
-- return a fully functional FieldData. Cannot work for all cases, should be
437
-- extended as necessary.
438
fillIncompleteFields :: (t1 -> t2 -> FieldDefinition,
439
                         t1 -> FieldGetter a b,
440
                         QffMode)
441
                     -> t1
442
                     -> t2
443
                     -> FieldData a b
444
fillIncompleteFields (iDef, iGet, mode) firstVal secondVal =
445
  (iDef firstVal secondVal, iGet firstVal, mode)
446

    
447
-- | Given indexed fields that describe lists, complete / instantiate them for
448
-- a given list size.
449
instantiateIndexedFields :: (Show t1, Integral t1)
450
                         => Int            -- ^ The size of the list
451
                         -> [(t1 -> String -> FieldDefinition,
452
                              t1 -> FieldGetter a b,
453
                              QffMode)]    -- ^ The indexed fields
454
                         -> FieldList a b  -- ^ A list of complete fields
455
instantiateIndexedFields listSize fields = do
456
  index <- take listSize [0..]
457
  field <- fields
458
  return . fillIncompleteFields field index . formatOrdinal $ index + 1
459

    
460
-- * Various helper functions for property retrieval
461

    
462
-- | Helper function for primary node retrieval
463
getPrimaryNode :: ConfigData -> Instance -> ErrorResult Node
464
getPrimaryNode cfg = getInstPrimaryNode cfg . instName
465

    
466
-- | Get primary node hostname
467
getPrimaryNodeName :: ConfigData -> Instance -> ResultEntry
468
getPrimaryNodeName cfg inst =
469
  rsErrorNoData $ nodeName <$> getPrimaryNode cfg inst
470

    
471
-- | Get primary node group
472
getPrimaryNodeGroup :: ConfigData -> Instance -> ErrorResult NodeGroup
473
getPrimaryNodeGroup cfg inst = do
474
  pNode <- getPrimaryNode cfg inst
475
  maybeToError "Configuration missing" $ getGroupOfNode cfg pNode
476

    
477
-- | Get primary node group name
478
getPrimaryNodeGroupName :: ConfigData -> Instance -> ResultEntry
479
getPrimaryNodeGroupName cfg inst =
480
  rsErrorNoData $ groupName <$> getPrimaryNodeGroup cfg inst
481

    
482
-- | Get primary node group uuid
483
getPrimaryNodeGroupUuid :: ConfigData -> Instance -> ResultEntry
484
getPrimaryNodeGroupUuid cfg inst =
485
  rsErrorNoData $ groupUuid <$> getPrimaryNodeGroup cfg inst
486

    
487
-- | Get secondary nodes - the configuration objects themselves
488
getSecondaryNodes :: ConfigData -> Instance -> ErrorResult [Node]
489
getSecondaryNodes cfg inst = do
490
  pNode <- getPrimaryNode cfg inst
491
  allNodes <- getInstAllNodes cfg $ instName inst
492
  return $ delete pNode allNodes
493

    
494
-- | Get attributes of the secondary nodes
495
getSecondaryNodeAttribute :: (J.JSON a)
496
                          => (Node -> a)
497
                          -> ConfigData
498
                          -> Instance
499
                          -> ResultEntry
500
getSecondaryNodeAttribute getter cfg inst =
501
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodes cfg inst
502

    
503
-- | Get secondary node groups
504
getSecondaryNodeGroups :: ConfigData -> Instance -> ErrorResult [NodeGroup]
505
getSecondaryNodeGroups cfg inst = do
506
  sNodes <- getSecondaryNodes cfg inst
507
  return . catMaybes $ map (getGroupOfNode cfg) sNodes
508

    
509
-- | Get attributes of secondary node groups
510
getSecondaryNodeGroupAttribute :: (J.JSON a)
511
                               => (NodeGroup -> a)
512
                               -> ConfigData
513
                               -> Instance
514
                               -> ResultEntry
515
getSecondaryNodeGroupAttribute getter cfg inst =
516
  rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodeGroups cfg inst
517

    
518
-- | Beparam getter builder: given a field, it returns a FieldConfig
519
-- getter, that is a function that takes the config and the object and
520
-- returns the Beparam field specified when the getter was built.
521
beParamGetter :: String       -- ^ The field we are building the getter for
522
              -> ConfigData   -- ^ The configuration object
523
              -> Instance     -- ^ The instance configuration object
524
              -> ResultEntry  -- ^ The result
525
beParamGetter field config inst =
526
  case getFilledInstBeParams config inst of
527
    Ok beParams -> dictFieldGetter field $ Just beParams
528
    Bad       _ -> rsNoData
529

    
530
-- | Hvparam getter builder: given a field, it returns a FieldConfig
531
-- getter, that is a function that takes the config and the object and
532
-- returns the Hvparam field specified when the getter was built.
533
hvParamGetter :: String -- ^ The field we're building the getter for
534
              -> ConfigData -> Instance -> ResultEntry
535
hvParamGetter field cfg inst =
536
  rsMaybeUnavail . Map.lookup field . fromContainer $
537
    getFilledInstHvParams (C.toList C.hvcGlobals) cfg inst
538

    
539
-- * Live fields functionality
540

    
541
-- | List of node live fields.
542
instanceLiveFieldsDefs :: [(FieldName, FieldTitle, FieldType, String, FieldDoc)]
543
instanceLiveFieldsDefs =
544
  [ ("oper_ram", "Memory", QFTUnit, "oper_ram",
545
     "Actual memory usage as seen by hypervisor")
546
  , ("oper_vcpus", "VCPUs", QFTNumber, "oper_vcpus",
547
     "Actual number of VCPUs as seen by hypervisor")
548
  ]
549

    
550
-- | Map each name to a function that extracts that value from the RPC result.
551
instanceLiveFieldExtract :: FieldName -> InstanceInfo -> Instance -> J.JSValue
552
instanceLiveFieldExtract "oper_ram"   info _ = J.showJSON $ instInfoMemory info
553
instanceLiveFieldExtract "oper_vcpus" info _ = J.showJSON $ instInfoVcpus info
554
instanceLiveFieldExtract n _ _ = J.showJSON $
555
  "The field " ++ n ++ " is not an expected or extractable live field!"
556

    
557
-- | Helper for extracting an instance live field from the RPC results.
558
instanceLiveRpcCall :: FieldName -> Runtime -> Instance -> ResultEntry
559
instanceLiveRpcCall fname (Right (Just (res, _), _)) inst =
560
  case instanceLiveFieldExtract fname res inst of
561
    J.JSNull -> rsNoData
562
    x        -> rsNormal x
563
instanceLiveRpcCall _ (Right (Nothing, _)) _ = rsUnavail
564
instanceLiveRpcCall _ (Left err) _ =
565
  ResultEntry (rpcErrorToStatus err) Nothing
566

    
567
-- | Builder for node live fields.
568
instanceLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc)
569
                         -> FieldData Instance Runtime
570
instanceLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
571
  ( FieldDefinition fname ftitle ftype fdoc
572
  , FieldRuntime $ instanceLiveRpcCall fname
573
  , QffNormal)
574

    
575
-- * Functionality related to status and operational status extraction
576

    
577
-- | The documentation text for the instance status field
578
statusDocText :: String
579
statusDocText =
580
  let si = show . instanceStatusToRaw :: InstanceStatus -> String
581
  in  "Instance status; " ++
582
      si Running ++
583
      " if instance is set to be running and actually is, " ++
584
      si StatusDown ++
585
      " if instance is stopped and is not running, " ++
586
      si WrongNode ++
587
      " if instance running, but not on its designated primary node, " ++
588
      si ErrorUp ++
589
      " if instance should be stopped, but is actually running, " ++
590
      si ErrorDown ++
591
      " if instance should run, but doesn't, " ++
592
      si NodeDown ++
593
      " if instance's primary node is down, " ++
594
      si NodeOffline ++
595
      " if instance's primary node is marked offline, " ++
596
      si StatusOffline ++
597
      " if instance is offline and does not use dynamic resources"
598

    
599
-- | Checks if the primary node of an instance is offline
600
isPrimaryOffline :: ConfigData -> Instance -> Bool
601
isPrimaryOffline cfg inst =
602
  let pNodeResult = getNode cfg $ instPrimaryNode inst
603
  in case pNodeResult of
604
     Ok pNode -> nodeOffline pNode
605
     Bad    _ -> error "Programmer error - result assumed to be OK is Bad!"
606

    
607
-- | Determines the status of a live instance
608
liveInstanceStatus :: (InstanceInfo, Bool) -> Instance -> InstanceStatus
609
liveInstanceStatus (_, foundOnPrimary) inst
610
  | not foundOnPrimary    = WrongNode
611
  | adminState == AdminUp = Running
612
  | otherwise             = ErrorUp
613
  where adminState = instAdminState inst
614

    
615
-- | Determines the status of a dead instance.
616
deadInstanceStatus :: Instance -> InstanceStatus
617
deadInstanceStatus inst =
618
  case instAdminState inst of
619
    AdminUp      -> ErrorDown
620
    AdminDown    -> StatusDown
621
    AdminOffline -> StatusOffline
622

    
623
-- | Determines the status of the instance, depending on whether it is possible
624
-- to communicate with its primary node, on which node it is, and its
625
-- configuration.
626
determineInstanceStatus :: ConfigData      -- ^ The configuration data
627
                        -> Runtime         -- ^ All the data from the live call
628
                        -> Instance        -- ^ Static instance configuration
629
                        -> InstanceStatus  -- ^ Result
630
determineInstanceStatus cfg res inst
631
  | isPrimaryOffline cfg inst = NodeOffline
632
  | otherwise = case res of
633
      Left _                   -> NodeDown
634
      Right (Just liveData, _) -> liveInstanceStatus liveData inst
635
      Right (Nothing, _)       -> deadInstanceStatus inst
636

    
637
-- | Extracts the instance status, retrieving it using the functions above and
638
-- transforming it into a 'ResultEntry'.
639
statusExtract :: ConfigData -> Runtime -> Instance -> ResultEntry
640
statusExtract cfg res inst =
641
  rsNormal . J.showJSON . instanceStatusToRaw $
642
    determineInstanceStatus cfg res inst
643

    
644
-- | Extracts the operational status of the instance.
645
operStatusExtract :: Runtime -> Instance -> ResultEntry
646
operStatusExtract res _ =
647
  rsMaybeNoData $ J.showJSON <$>
648
    case res of
649
      Left _       -> Nothing
650
      Right (x, _) -> Just $ isJust x
651

    
652
-- | Extracts the console connection information
653
consoleExtract :: Runtime -> Instance -> ResultEntry
654
consoleExtract (Left err) _ = ResultEntry (rpcErrorToStatus err) Nothing
655
consoleExtract (Right (_, val)) _ = rsMaybeNoData val
656

    
657
-- * Helper functions extracting information as necessary for the generic query
658
-- interfaces
659

    
660
-- | This function checks if a node with a given uuid has experienced an error
661
-- or not.
662
checkForNodeError :: [(String, ERpcError a)]
663
                  -> String
664
                  -> Maybe RpcError
665
checkForNodeError uuidList uuid =
666
  case snd <$> pickPairUnique uuid uuidList of
667
    Just (Left err) -> Just err
668
    Just (Right _)  -> Nothing
669
    Nothing         -> Just . RpcResultError $
670
                         "Node response not present"
671

    
672
-- | Finds information about the instance in the info delivered by a node
673
findInfoInNodeResult :: Instance
674
                     -> ERpcError RpcResultAllInstancesInfo
675
                     -> Maybe InstanceInfo
676
findInfoInNodeResult inst nodeResponse =
677
  case nodeResponse of
678
    Left  _err    -> Nothing
679
    Right allInfo ->
680
      let instances = rpcResAllInstInfoInstances allInfo
681
          maybeMatch = pickPairUnique (instName inst) instances
682
      in snd <$> maybeMatch
683

    
684
-- | Retrieves the instance information if it is present anywhere in the all
685
-- instances RPC result. Notes if it originates from the primary node.
686
-- An error is delivered if there is no result, and the primary node is down.
687
getInstanceInfo :: [(String, ERpcError RpcResultAllInstancesInfo)]
688
                -> Instance
689
                -> ERpcError (Maybe (InstanceInfo, Bool))
690
getInstanceInfo uuidList inst =
691
  let pNodeUuid = instPrimaryNode inst
692
      primarySearchResult =
693
        pickPairUnique pNodeUuid uuidList >>= findInfoInNodeResult inst . snd
694
  in case primarySearchResult of
695
       Just instInfo -> Right . Just $ (instInfo, True)
696
       Nothing       ->
697
         let allSearchResult =
698
               getFirst . mconcat $ map
699
               (First . findInfoInNodeResult inst . snd) uuidList
700
         in case allSearchResult of
701
              Just instInfo -> Right . Just $ (instInfo, False)
702
              Nothing       ->
703
                case checkForNodeError uuidList pNodeUuid of
704
                  Just err -> Left err
705
                  Nothing  -> Right Nothing
706

    
707
-- | Retrieves the console information if present anywhere in the given results
708
getConsoleInfo :: [(String, ERpcError RpcResultInstanceConsoleInfo)]
709
               -> Instance
710
               -> Maybe InstanceConsoleInfo
711
getConsoleInfo uuidList inst =
712
  let allValidResults = concatMap rpcResInstConsInfoInstancesInfo .
713
                        rights . map snd $ uuidList
714
  in snd <$> pickPairUnique (instName inst) allValidResults
715

    
716
-- | Extracts all the live information that can be extracted.
717
extractLiveInfo :: [(Node, ERpcError RpcResultAllInstancesInfo)]
718
                -> [(Node, ERpcError RpcResultInstanceConsoleInfo)]
719
                -> Instance
720
                -> Runtime
721
extractLiveInfo nodeResultList nodeConsoleList inst =
722
  let uuidConvert     = map (\(x, y) -> (nodeUuid x, y))
723
      uuidResultList  = uuidConvert nodeResultList
724
      uuidConsoleList = uuidConvert nodeConsoleList
725
  in case getInstanceInfo uuidResultList inst of
726
    -- If we can't get the instance info, we can't get the console info either.
727
    -- Best to propagate the error further.
728
    Left err  -> Left err
729
    Right res -> Right (res, getConsoleInfo uuidConsoleList inst)
730

    
731
-- | Retrieves all the parameters for the console calls.
732
getAllConsoleParams :: ConfigData
733
                    -> [Instance]
734
                    -> ErrorResult [InstanceConsoleInfoParams]
735
getAllConsoleParams cfg instances = do
736
  pNodes <- mapM (getPrimaryNode cfg) instances
737
  let filledHvParams = map (getFilledInstHvParams [] cfg) instances
738
  filledBeParams <- mapM (getFilledInstBeParams cfg) instances
739
  return . map (\(i, n, h, b) -> InstanceConsoleInfoParams i n h b) $
740
    zip4 instances pNodes filledHvParams filledBeParams
741

    
742
-- | Compares two params according to their node, needed for grouping.
743
compareParamsByNode :: InstanceConsoleInfoParams
744
                    -> InstanceConsoleInfoParams
745
                    -> Bool
746
compareParamsByNode x y = instConsInfoParamsNode x == instConsInfoParamsNode y
747

    
748
-- | Groups instance information calls heading out to the same nodes.
749
consoleParamsToCalls :: [InstanceConsoleInfoParams]
750
                     -> [(Node, RpcCallInstanceConsoleInfo)]
751
consoleParamsToCalls params =
752
  let sortedParams = sortBy
753
        (comparing (instPrimaryNode . instConsInfoParamsInstance)) params
754
      groupedParams = groupBy compareParamsByNode sortedParams
755
  in map (\x -> case x of
756
            [] -> error "Programmer error: group must have one or more members"
757
            paramGroup@(y:_) ->
758
              let node = instConsInfoParamsNode y
759
                  packer z = (instName $ instConsInfoParamsInstance z, z)
760
              in (node, RpcCallInstanceConsoleInfo . map packer $ paramGroup)
761
         ) groupedParams
762

    
763
-- | Retrieves a list of all the hypervisors and params used by the given
764
-- instances.
765
getHypervisorSpecs :: ConfigData -> [Instance] -> [(Hypervisor, HvParams)]
766
getHypervisorSpecs cfg instances =
767
  let hvs = nub . map instHypervisor $ instances
768
      hvParamMap = (fromContainer . clusterHvparams . configCluster $ cfg)
769
  in zip hvs . map ((Map.!) hvParamMap . hypervisorToRaw) $ hvs
770

    
771
-- | Collect live data from RPC query if enabled.
772
collectLiveData :: Bool -> ConfigData -> [Instance] -> IO [(Instance, Runtime)]
773
collectLiveData liveDataEnabled cfg instances
774
  | not liveDataEnabled = return . zip instances . repeat . Left .
775
                            RpcResultError $ "Live data disabled"
776
  | otherwise = do
777
      let hvSpecs = getHypervisorSpecs cfg instances
778
          instanceNodes = nub . justOk $
779
                            map (getNode cfg . instPrimaryNode) instances
780
          goodNodes = nodesWithValidConfig cfg instanceNodes
781
      instInfoRes <- executeRpcCall goodNodes (RpcCallAllInstancesInfo hvSpecs)
782
      consInfoRes <- case getAllConsoleParams cfg instances of
783
        Bad _ -> return . zip goodNodes . repeat . Left $ RpcResultError
784
                   "Cannot construct parameters for console info call"
785
        Ok  p -> executeRpcCalls $ consoleParamsToCalls p
786
      return . zip instances .
787
        map (extractLiveInfo instInfoRes consInfoRes) $ instances