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 |