module Ganeti.Query.Instance
( Runtime
, fieldsMap
, collectLiveData
, getInstanceInfo
, instanceFields
, instanceAliases
) where
import Control.Applicative
import Control.Monad (liftM, (>=>))
import qualified Data.ByteString.UTF8 as UTF8
import Data.Either
import Data.List
import Data.Maybe
import Data.Monoid
import qualified Data.Map as Map
import Data.Ord (comparing)
import qualified Text.JSON as J
import Text.Printf
import Ganeti.BasicTypes
import Ganeti.Common
import Ganeti.Config
import qualified Ganeti.Constants as C
import qualified Ganeti.ConstantUtils as C
import Ganeti.Errors
import Ganeti.JSON
import Ganeti.Objects
import Ganeti.Query.Common
import Ganeti.Query.Language
import Ganeti.Query.Types
import Ganeti.Rpc
import Ganeti.Storage.Utils
import Ganeti.Types
import Ganeti.Utils (formatOrdinal)
type LiveInfo = (Maybe (InstanceInfo, Bool), Maybe InstanceConsoleInfo)
type Runtime = Either RpcError LiveInfo
fieldsMap :: FieldMap Instance Runtime
fieldsMap = fieldListToFieldMap aliasedFields
instanceAliases :: [(FieldName, FieldName)]
instanceAliases =
[ ("vcpus", "be/vcpus")
, ("be/memory", "be/maxmem")
, ("sda_size", "disk.size/0")
, ("sdb_size", "disk.size/1")
, ("ip", "nic.ip/0")
, ("mac", "nic.mac/0")
, ("bridge", "nic.bridge/0")
, ("nic_mode", "nic.mode/0")
, ("nic_link", "nic.link/0")
, ("nic_network", "nic.network/0")
]
aliasedFields :: FieldList Instance Runtime
aliasedFields = aliasFields instanceAliases instanceFields
instanceFields :: FieldList Instance Runtime
instanceFields =
[ (FieldDefinition "admin_state" "InstanceState" QFTText
"Desired state of instance",
FieldSimple (rsMaybeNoData . liftM adminStateToRaw . instAdminState),
QffNormal)
, (FieldDefinition "admin_state_source" "InstanceStateSource" QFTText
"Who last changed the desired state of the instance",
FieldSimple (rsMaybeNoData . liftM adminStateSourceToRaw
. instAdminStateSource),
QffNormal)
, (FieldDefinition "admin_up" "Autostart" QFTBool
"Desired state of instance",
FieldSimple (rsMaybeNoData . liftM (== AdminUp) . instAdminState),
QffNormal)
, (FieldDefinition "disks_active" "DisksActive" QFTBool
"Desired state of instance disks",
FieldSimple (rsMaybeNoData . instDisksActive), QffNormal)
, (FieldDefinition "name" "Instance" QFTText
"Instance name",
FieldSimple (rsMaybeNoData . instName), QffHostname)
, (FieldDefinition "hypervisor" "Hypervisor" QFTText
"Hypervisor name",
FieldSimple (rsMaybeNoData . instHypervisor), QffNormal)
, (FieldDefinition "network_port" "Network_port" QFTOther
"Instance network port if available (e.g. for VNC console)",
FieldSimple (rsMaybeUnavail . instNetworkPort), QffNormal)
, (FieldDefinition "os" "OS" QFTText
"Operating system",
FieldSimple (rsMaybeNoData . instOs), QffNormal)
, (FieldDefinition "pnode" "Primary_node" QFTText
"Primary node",
FieldConfig getPrimaryNodeName, QffHostname)
, (FieldDefinition "pnode.group" "PrimaryNodeGroup" QFTText
"Primary node's group",
FieldConfig getPrimaryNodeGroupName, QffNormal)
, (FieldDefinition "pnode.group.uuid" "PrimaryNodeGroupUUID" QFTText
"Primary node's group UUID",
FieldConfig getPrimaryNodeGroupUuid, QffNormal)
, (FieldDefinition "snodes" "Secondary_Nodes" QFTOther
"Secondary nodes; usually this will just be one node",
FieldConfig (getSecondaryNodeAttribute nodeName), QffNormal)
, (FieldDefinition "snodes.group" "SecondaryNodesGroups" QFTOther
"Node groups of secondary nodes",
FieldConfig (getSecondaryNodeGroupAttribute groupName), QffNormal)
, (FieldDefinition "snodes.group.uuid" "SecondaryNodesGroupsUUID" QFTOther
"Node group UUIDs of secondary nodes",
FieldConfig (getSecondaryNodeGroupAttribute uuidOf), QffNormal)
] ++
[ (FieldDefinition "hvparams" "HypervisorParameters" QFTOther
"Hypervisor parameters (merged)",
FieldConfig
((rsNormal .) . getFilledInstHvParams (C.toList C.hvcGlobals)),
QffNormal),
(FieldDefinition "beparams" "BackendParameters" QFTOther
"Backend parameters (merged)",
FieldConfig ((rsErrorNoData .) . getFilledInstBeParams), QffNormal)
, (FieldDefinition "osparams" "OpSysParameters" QFTOther
"Operating system parameters (merged)",
FieldConfig ((rsNormal .) . getFilledInstOsParams), QffNormal)
, (FieldDefinition "custom_hvparams" "CustomHypervisorParameters" QFTOther
"Custom hypervisor parameters",
FieldSimple (rsNormal . instHvparams), QffNormal)
, (FieldDefinition "custom_beparams" "CustomBackendParameters" QFTOther
"Custom backend parameters",
FieldSimple (rsNormal . instBeparams), QffNormal)
, (FieldDefinition "custom_osparams" "CustomOpSysParameters" QFTOther
"Custom operating system parameters",
FieldSimple (rsNormal . instOsparams), QffNormal)
, (FieldDefinition "custom_nicparams" "CustomNicParameters" QFTOther
"Custom network interface parameters",
FieldSimple (rsNormal . map nicNicparams . instNics), QffNormal)
] ++
map (buildBeParamField beParamGetter) allBeParamFields ++
map (buildHvParamField hvParamGetter)
(C.toList C.hvsParameters \\ C.toList C.hvcGlobals) ++
[ (FieldDefinition "disk_usage" "DiskUsage" QFTUnit
"Total disk space used by instance on each of its nodes; this is not the\
\ disk size visible to the instance, but the usage on the node",
FieldConfig getDiskSizeRequirements, QffNormal)
, (FieldDefinition "disk.count" "Disks" QFTNumber
"Number of disks",
FieldSimple (rsNormal . length . instDisks), QffNormal)
, (FieldDefinition "disk.sizes" "Disk_sizes" QFTOther
"List of disk sizes", FieldConfig getDiskSizes, QffNormal)
, (FieldDefinition "disk.spindles" "Disk_spindles" QFTOther
"List of disk spindles",
FieldConfig getDiskSpindles, QffNormal)
, (FieldDefinition "disk.names" "Disk_names" QFTOther
"List of disk names",
FieldConfig getDiskNames, QffNormal)
, (FieldDefinition "disk.uuids" "Disk_UUIDs" QFTOther
"List of disk UUIDs",
FieldConfig getDiskUuids, QffNormal)
, (FieldDefinition "disk_template" "Disk_template" QFTText
"Instance disk template",
FieldConfig getDiskTemplate, QffNormal)
] ++
instantiateIndexedFields C.maxDisks
[ (fieldDefinitionCompleter "disk.size/%d" "Disk/%d" QFTUnit
"Disk size of %s disk",
getIndexedOptionalConfField getInstDisksFromObj diskSize,
QffNormal)
, (fieldDefinitionCompleter "disk.spindles/%d" "DiskSpindles/%d" QFTNumber
"Spindles of %s disk",
getIndexedOptionalConfField getInstDisksFromObj diskSpindles,
QffNormal)
, (fieldDefinitionCompleter "disk.name/%d" "DiskName/%d" QFTText
"Name of %s disk",
getIndexedOptionalConfField getInstDisksFromObj diskName, QffNormal)
, (fieldDefinitionCompleter "disk.uuid/%d" "DiskUUID/%d" QFTText
"UUID of %s disk",
getIndexedConfField getInstDisksFromObj uuidOf, QffNormal)
] ++
[ (FieldDefinition "nic.count" "NICs" QFTNumber
"Number of network interfaces",
FieldSimple (rsNormal . length . instNics), QffNormal)
, (FieldDefinition "nic.macs" "NIC_MACs" QFTOther
(nicAggDescPrefix ++ "MAC address"),
FieldSimple (rsNormal . map nicMac . instNics), QffNormal)
, (FieldDefinition "nic.ips" "NIC_IPs" QFTOther
(nicAggDescPrefix ++ "IP address"),
FieldSimple (rsNormal . map (MaybeForJSON . nicIp) . instNics),
QffNormal)
, (FieldDefinition "nic.names" "NIC_Names" QFTOther
(nicAggDescPrefix ++ "name"),
FieldSimple (rsNormal . map (MaybeForJSON . nicName) . instNics),
QffNormal)
, (FieldDefinition "nic.uuids" "NIC_UUIDs" QFTOther
(nicAggDescPrefix ++ "UUID"),
FieldSimple (rsNormal . map uuidOf . instNics), QffNormal)
, (FieldDefinition "nic.modes" "NIC_modes" QFTOther
(nicAggDescPrefix ++ "mode"),
FieldConfig (\cfg -> rsNormal . map
(nicpMode . fillNicParamsFromConfig cfg . nicNicparams)
. instNics),
QffNormal)
, (FieldDefinition "nic.vlans" "NIC_VLANs" QFTOther
(nicAggDescPrefix ++ "VLAN"),
FieldConfig (\cfg -> rsNormal . map (MaybeForJSON . getNicVlan .
fillNicParamsFromConfig cfg . nicNicparams) . instNics),
QffNormal)
, (FieldDefinition "nic.bridges" "NIC_bridges" QFTOther
(nicAggDescPrefix ++ "bridge"),
FieldConfig (\cfg -> rsNormal . map (MaybeForJSON . getNicBridge .
fillNicParamsFromConfig cfg . nicNicparams) . instNics),
QffNormal)
, (FieldDefinition "nic.links" "NIC_links" QFTOther
(nicAggDescPrefix ++ "link"),
FieldConfig (\cfg -> rsNormal . map
(nicpLink . fillNicParamsFromConfig cfg . nicNicparams)
. instNics), QffNormal)
, (FieldDefinition "nic.networks" "NIC_networks" QFTOther
"List containing each interface's network",
FieldSimple (rsNormal . map (MaybeForJSON . nicNetwork) . instNics),
QffNormal)
, (FieldDefinition "nic.networks.names" "NIC_networks_names" QFTOther
"List containing the name of each interface's network",
FieldConfig (\cfg -> rsNormal . map
(\x -> MaybeForJSON (getNetworkName cfg <$> nicNetwork x))
. instNics),
QffNormal)
] ++
instantiateIndexedFields C.maxNics
[ (fieldDefinitionCompleter "nic.ip/%d" "NicIP/%d" QFTText
("IP address" ++ nicDescSuffix),
getIndexedOptionalField instNics nicIp, QffNormal)
, (fieldDefinitionCompleter "nic.uuid/%d" "NicUUID/%d" QFTText
("UUID address" ++ nicDescSuffix),
getIndexedField instNics uuidOf, QffNormal)
, (fieldDefinitionCompleter "nic.mac/%d" "NicMAC/%d" QFTText
("MAC address" ++ nicDescSuffix),
getIndexedField instNics nicMac, QffNormal)
, (fieldDefinitionCompleter "nic.name/%d" "NicName/%d" QFTText
("Name address" ++ nicDescSuffix),
getIndexedOptionalField instNics nicName, QffNormal)
, (fieldDefinitionCompleter "nic.network/%d" "NicNetwork/%d" QFTText
("Network" ++ nicDescSuffix),
getIndexedOptionalField instNics nicNetwork, QffNormal)
, (fieldDefinitionCompleter "nic.mode/%d" "NicMode/%d" QFTText
("Mode" ++ nicDescSuffix),
getIndexedNicField nicpMode, QffNormal)
, (fieldDefinitionCompleter "nic.link/%d" "NicLink/%d" QFTText
("Link" ++ nicDescSuffix),
getIndexedNicField nicpLink, QffNormal)
, (fieldDefinitionCompleter "nic.vlan/%d" "NicVLAN/%d" QFTText
("VLAN" ++ nicDescSuffix),
getOptionalIndexedNicField getNicVlan, QffNormal)
, (fieldDefinitionCompleter "nic.network.name/%d" "NicNetworkName/%d" QFTText
("Network name" ++ nicDescSuffix),
getIndexedNicNetworkNameField, QffNormal)
, (fieldDefinitionCompleter "nic.bridge/%d" "NicBridge/%d" QFTText
("Bridge" ++ nicDescSuffix),
getOptionalIndexedNicField getNicBridge, QffNormal)
] ++
[ (FieldDefinition "status" "Status" QFTText
statusDocText,
FieldConfigRuntime statusExtract, QffNormal)
, (FieldDefinition "oper_state" "Running" QFTBool
"Actual state of instance",
FieldRuntime operStatusExtract, QffNormal),
(FieldDefinition "console" "Console" QFTOther
"Instance console information",
FieldRuntime consoleExtract, QffNormal)
] ++
map instanceLiveFieldBuilder instanceLiveFieldsDefs ++
timeStampFields ++
serialFields "Instance" ++
uuidFields "Instance" ++
forthcomingFields "Instance" ++
tagsFields
nicDescSuffix ::String
nicDescSuffix = " of %s network interface"
nicAggDescPrefix ::String
nicAggDescPrefix = "List containing each network interface's "
getNetworkName :: ConfigData -> String -> NonEmptyString
getNetworkName cfg = networkName . (Map.!) (fromContainer $ configNetworks cfg)
. UTF8.fromString
getNicBridge :: FilledNicParams -> Maybe String
getNicBridge nicParams
| nicpMode nicParams == NMBridged = Just $ nicpLink nicParams
| otherwise = Nothing
getNicVlan :: FilledNicParams -> Maybe String
getNicVlan params
| nicpMode params == NMOvs = Just $ nicpVlan params
| otherwise = Nothing
fillNicParamsFromConfig :: ConfigData -> PartialNicParams -> FilledNicParams
fillNicParamsFromConfig cfg = fillParams (getDefaultNicParams cfg)
getDefaultNicParams :: ConfigData -> FilledNicParams
getDefaultNicParams cfg =
(Map.!) (fromContainer . clusterNicparams . configCluster $ cfg)
$ UTF8.fromString C.ppDefault
getDiskSizeRequirements :: ConfigData -> Instance -> ResultEntry
getDiskSizeRequirements cfg inst =
rsErrorNoData . liftA (sum . map getSize) . getInstDisksFromObj cfg $ inst
where
diskType x = lidDiskType <$> diskLogicalId x
getSize :: Disk -> Int
getSize disk =
let dt = diskType disk
in case dt of
Just DTDrbd8 -> fromMaybe 0 (diskSize disk) + C.drbdMetaSize
Just DTDiskless -> 0
Just DTBlock -> 0
_ -> fromMaybe 0 (diskSize disk)
getDiskSizes :: ConfigData -> Instance -> ResultEntry
getDiskSizes cfg =
rsErrorNoData . liftA (map $ MaybeForJSON . diskSize)
. getInstDisksFromObj cfg
getDiskSpindles :: ConfigData -> Instance -> ResultEntry
getDiskSpindles cfg =
rsErrorNoData . liftA (map (MaybeForJSON . diskSpindles)) .
getInstDisksFromObj cfg
getDiskNames :: ConfigData -> Instance -> ResultEntry
getDiskNames cfg =
rsErrorNoData . liftA (map (MaybeForJSON . diskName)) .
getInstDisksFromObj cfg
getDiskUuids :: ConfigData -> Instance -> ResultEntry
getDiskUuids cfg =
rsErrorNoData . liftA (map uuidOf) . getInstDisksFromObj cfg
getIndexedOptionalConfField :: (J.JSON b)
=> (ConfigData -> Instance -> ErrorResult [a])
-> (a -> Maybe b)
-> Int
-> FieldGetter Instance Runtime
getIndexedOptionalConfField extractor optPropertyGetter index =
let getProperty x = maybeAt index x >>= optPropertyGetter
in FieldConfig (\cfg ->
rsErrorMaybeUnavail . liftA getProperty . extractor cfg)
getIndexedConfField :: (J.JSON b)
=> (ConfigData -> Instance -> ErrorResult [a])
-> (a -> b)
-> Int
-> FieldGetter Instance Runtime
getIndexedConfField extractor propertyGetter index =
let optPropertyGetter = Just . propertyGetter
in getIndexedOptionalConfField extractor optPropertyGetter index
getIndexedNicNetworkNameField :: Int -> FieldGetter Instance Runtime
getIndexedNicNetworkNameField index =
FieldConfig (\cfg inst -> rsMaybeUnavail $ do
nicObj <- maybeAt index $ instNics inst
nicNetworkId <- nicNetwork nicObj
return $ getNetworkName cfg nicNetworkId)
getIndexedNicField :: (J.JSON a)
=> (FilledNicParams -> a)
-> Int
-> FieldGetter Instance Runtime
getIndexedNicField getter =
getOptionalIndexedNicField (\x -> Just . getter $ x)
getOptionalIndexedNicField :: (J.JSON a)
=> (FilledNicParams -> Maybe a)
-> Int
-> FieldGetter Instance Runtime
getOptionalIndexedNicField =
getIndexedFieldWithDefault
(map nicNicparams . instNics) (\x _ -> getDefaultNicParams x) fillParams
getIndexedFieldWithDefault :: (J.JSON c)
=> (Instance -> [a])
-> (ConfigData -> Instance -> b)
-> (b -> a -> b)
-> (b -> Maybe c)
-> Int
-> FieldGetter Instance Runtime
getIndexedFieldWithDefault
listGetter defaultGetter fillFn propertyGetter index =
FieldConfig (\cfg inst -> rsMaybeUnavail $ do
incompleteObj <- maybeAt index $ listGetter inst
let defaultObj = defaultGetter cfg inst
completeObj = fillFn defaultObj incompleteObj
propertyGetter completeObj)
getIndexedOptionalField :: (J.JSON b)
=> (Instance -> [a])
-> (a -> Maybe b)
-> Int
-> FieldGetter Instance Runtime
getIndexedOptionalField extractor optPropertyGetter index =
FieldSimple(\inst -> rsMaybeUnavail $ do
obj <- maybeAt index $ extractor inst
optPropertyGetter obj)
getIndexedField :: (J.JSON b)
=> (Instance -> [a])
-> (a -> b)
-> Int
-> FieldGetter Instance Runtime
getIndexedField extractor propertyGetter index =
let optPropertyGetter = Just . propertyGetter
in getIndexedOptionalField extractor optPropertyGetter index
maybeAt :: Int -> [a] -> Maybe a
maybeAt index list
| index >= length list = Nothing
| otherwise = Just $ list !! index
fieldDefinitionCompleter :: (PrintfArg t1) => (PrintfArg t2)
=> FieldName
-> FieldTitle
-> FieldType
-> FieldDoc
-> t1
-> t2
-> FieldDefinition
fieldDefinitionCompleter fName fTitle fType fDoc firstVal secondVal =
FieldDefinition (printf fName firstVal)
(printf fTitle firstVal)
fType
(printf fDoc secondVal)
fillIncompleteFields :: (t1 -> t2 -> FieldDefinition,
t1 -> FieldGetter a b,
QffMode)
-> t1
-> t2
-> FieldData a b
fillIncompleteFields (iDef, iGet, mode) firstVal secondVal =
(iDef firstVal secondVal, iGet firstVal, mode)
instantiateIndexedFields :: (Show t1, Integral t1)
=> Int
-> [(t1 -> String -> FieldDefinition,
t1 -> FieldGetter a b,
QffMode)]
-> FieldList a b
instantiateIndexedFields listSize fields = do
index <- take listSize [0..]
field <- fields
return . fillIncompleteFields field index . formatOrdinal $ index + 1
getPrimaryNode :: ConfigData -> Instance -> ErrorResult Node
getPrimaryNode cfg = maybe (Bad $ ParameterError "no primary node") return
. instName
>=> getInstPrimaryNode cfg
getPrimaryNodeName :: ConfigData -> Instance -> ResultEntry
getPrimaryNodeName cfg inst =
rsErrorNoData $ nodeName <$> getPrimaryNode cfg inst
getPrimaryNodeGroup :: ConfigData -> Instance -> ErrorResult NodeGroup
getPrimaryNodeGroup cfg inst = do
pNode <- getPrimaryNode cfg inst
maybeToError "Configuration missing" $ getGroupOfNode cfg pNode
getPrimaryNodeGroupName :: ConfigData -> Instance -> ResultEntry
getPrimaryNodeGroupName cfg inst =
rsErrorNoData $ groupName <$> getPrimaryNodeGroup cfg inst
getPrimaryNodeGroupUuid :: ConfigData -> Instance -> ResultEntry
getPrimaryNodeGroupUuid cfg inst =
rsErrorNoData $ uuidOf <$> getPrimaryNodeGroup cfg inst
getSecondaryNodes :: ConfigData -> Instance -> ErrorResult [Node]
getSecondaryNodes cfg inst = do
pNode <- getPrimaryNode cfg inst
iname <- maybe (Bad $ ParameterError "no name") return $ instName inst
allNodes <- getInstAllNodes cfg iname
return $ delete pNode allNodes
getSecondaryNodeAttribute :: (J.JSON a)
=> (Node -> a)
-> ConfigData
-> Instance
-> ResultEntry
getSecondaryNodeAttribute getter cfg inst =
rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodes cfg inst
getSecondaryNodeGroups :: ConfigData -> Instance -> ErrorResult [NodeGroup]
getSecondaryNodeGroups cfg inst = do
sNodes <- getSecondaryNodes cfg inst
return . catMaybes $ map (getGroupOfNode cfg) sNodes
getSecondaryNodeGroupAttribute :: (J.JSON a)
=> (NodeGroup -> a)
-> ConfigData
-> Instance
-> ResultEntry
getSecondaryNodeGroupAttribute getter cfg inst =
rsErrorNoData $ map (J.showJSON . getter) <$> getSecondaryNodeGroups cfg inst
beParamGetter :: String
-> ConfigData
-> Instance
-> ResultEntry
beParamGetter field config inst =
case getFilledInstBeParams config inst of
Ok beParams -> dictFieldGetter field $ Just beParams
Bad _ -> rsNoData
hvParamGetter :: String
-> ConfigData -> Instance -> ResultEntry
hvParamGetter field cfg inst =
rsMaybeUnavail . Map.lookup (UTF8.fromString field) . fromContainer $
getFilledInstHvParams (C.toList C.hvcGlobals) cfg inst
instanceLiveFieldsDefs :: [(FieldName, FieldTitle, FieldType, String, FieldDoc)]
instanceLiveFieldsDefs =
[ ("oper_ram", "Memory", QFTUnit, "oper_ram",
"Actual memory usage as seen by hypervisor")
, ("oper_vcpus", "VCPUs", QFTNumber, "oper_vcpus",
"Actual number of VCPUs as seen by hypervisor")
]
instanceLiveFieldExtract :: FieldName -> InstanceInfo -> Instance -> J.JSValue
instanceLiveFieldExtract "oper_ram" info _ = J.showJSON $ instInfoMemory info
instanceLiveFieldExtract "oper_vcpus" info _ = J.showJSON $ instInfoVcpus info
instanceLiveFieldExtract n _ _ = J.showJSON $
"The field " ++ n ++ " is not an expected or extractable live field!"
instanceLiveRpcCall :: FieldName -> Runtime -> Instance -> ResultEntry
instanceLiveRpcCall fname (Right (Just (res, _), _)) inst =
case instanceLiveFieldExtract fname res inst of
J.JSNull -> rsNoData
x -> rsNormal x
instanceLiveRpcCall _ (Right (Nothing, _)) _ = rsUnavail
instanceLiveRpcCall _ (Left err) _ =
ResultEntry (rpcErrorToStatus err) Nothing
instanceLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc)
-> FieldData Instance Runtime
instanceLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
( FieldDefinition fname ftitle ftype fdoc
, FieldRuntime $ instanceLiveRpcCall fname
, QffNormal)
statusDocText :: String
statusDocText =
let si = show . instanceStatusToRaw :: InstanceStatus -> String
in "Instance status; " ++
si Running ++
" if instance is set to be running and actually is, " ++
si StatusDown ++
" if instance is stopped and is not running, " ++
si WrongNode ++
" if instance running, but not on its designated primary node, " ++
si ErrorUp ++
" if instance should be stopped, but is actually running, " ++
si ErrorDown ++
" if instance should run, but doesn't, " ++
si NodeDown ++
" if instance's primary node is down, " ++
si NodeOffline ++
" if instance's primary node is marked offline, " ++
si StatusOffline ++
" if instance is offline and does not use dynamic resources"
isPrimaryOffline :: ConfigData -> Instance -> Bool
isPrimaryOffline cfg inst =
let pNodeResult = maybe (Bad $ ParameterError "no primary node") return
(instPrimaryNode inst)
>>= getNode cfg
in case pNodeResult of
Ok pNode -> nodeOffline pNode
Bad _ -> error "Programmer error - result assumed to be OK is Bad!"
userShutdownEnabled :: ConfigData -> Bool
userShutdownEnabled = clusterEnabledUserShutdown . configCluster
liveInstanceStatus :: ConfigData
-> (InstanceInfo, Bool)
-> Instance
-> InstanceStatus
liveInstanceStatus cfg (instInfo, foundOnPrimary) inst
| not foundOnPrimary = WrongNode
| otherwise =
case instanceState of
InstanceStateRunning
| adminState == Just AdminUp -> Running
| otherwise -> ErrorUp
InstanceStateShutdown
| adminState == Just AdminUp && allowDown -> UserDown
| adminState == Just AdminUp -> ErrorDown
| otherwise -> StatusDown
where adminState = instAdminState inst
instanceState = instInfoState instInfo
hvparams =
fromContainer $ getFilledInstHvParams (C.toList C.hvcGlobals) cfg inst
allowDown =
userShutdownEnabled cfg &&
(instHypervisor inst /= Just Kvm ||
(Map.member (UTF8.fromString C.hvKvmUserShutdown) hvparams &&
hvparams Map.! UTF8.fromString C.hvKvmUserShutdown
== J.JSBool True))
deadInstanceStatus :: ConfigData -> Instance -> InstanceStatus
deadInstanceStatus cfg inst =
case instAdminState inst of
Just AdminUp -> ErrorDown
Just AdminDown | wasCleanedUp && userShutdownEnabled cfg -> UserDown
| otherwise -> StatusDown
Just AdminOffline -> StatusOffline
Nothing -> StatusDown
where wasCleanedUp = instAdminStateSource inst == Just UserSource
determineInstanceStatus :: ConfigData
-> Runtime
-> Instance
-> InstanceStatus
determineInstanceStatus cfg res inst
| isPrimaryOffline cfg inst = NodeOffline
| otherwise = case res of
Left _ -> NodeDown
Right (Just liveData, _) -> liveInstanceStatus cfg liveData inst
Right (Nothing, _) -> deadInstanceStatus cfg inst
statusExtract :: ConfigData -> Runtime -> Instance -> ResultEntry
statusExtract cfg res inst =
rsNormal . J.showJSON . instanceStatusToRaw $
determineInstanceStatus cfg res inst
operStatusExtract :: Runtime -> Instance -> ResultEntry
operStatusExtract res _ =
rsMaybeNoData $ J.showJSON <$>
case res of
Left _ -> Nothing
Right (x, _) -> Just $ isJust x
consoleExtract :: Runtime -> Instance -> ResultEntry
consoleExtract (Left err) _ = ResultEntry (rpcErrorToStatus err) Nothing
consoleExtract (Right (_, val)) _ = rsMaybeNoData val
checkForNodeError :: [(String, ERpcError a)]
-> String
-> Maybe RpcError
checkForNodeError uuidList uuid =
case snd <$> pickPairUnique uuid uuidList of
Just (Left err) -> Just err
Just (Right _) -> Nothing
Nothing -> Just . RpcResultError $
"Node response not present"
findInfoInNodeResult :: Instance
-> ERpcError RpcResultAllInstancesInfo
-> Maybe InstanceInfo
findInfoInNodeResult inst nodeResponse =
case nodeResponse of
Left _err -> Nothing
Right allInfo ->
let instances = rpcResAllInstInfoInstances allInfo
maybeMatch = instName inst >>= (`pickPairUnique` instances)
in snd <$> maybeMatch
getInstanceInfo :: [(String, ERpcError RpcResultAllInstancesInfo)]
-> Instance
-> ERpcError (Maybe (InstanceInfo, Bool))
getInstanceInfo uuidList inst =
case instPrimaryNode inst of
Nothing -> Right Nothing
Just pNodeUuid ->
let primarySearchResult =
pickPairUnique pNodeUuid uuidList >>= findInfoInNodeResult inst
. snd
in case primarySearchResult of
Just instInfo -> Right . Just $ (instInfo, True)
Nothing ->
let allSearchResult =
getFirst . mconcat $ map
(First . findInfoInNodeResult inst . snd) uuidList
in case allSearchResult of
Just instInfo -> Right . Just $ (instInfo, False)
Nothing ->
case checkForNodeError uuidList pNodeUuid of
Just err -> Left err
Nothing -> Right Nothing
getConsoleInfo :: [(String, ERpcError RpcResultInstanceConsoleInfo)]
-> Instance
-> Maybe InstanceConsoleInfo
getConsoleInfo uuidList inst =
let allValidResults = concatMap rpcResInstConsInfoInstancesInfo .
rights . map snd $ uuidList
in snd <$> (instName inst >>= flip pickPairUnique allValidResults)
extractLiveInfo :: [(Node, ERpcError RpcResultAllInstancesInfo)]
-> [(Node, ERpcError RpcResultInstanceConsoleInfo)]
-> Instance
-> Runtime
extractLiveInfo nodeResultList nodeConsoleList inst =
let uuidConvert = map (\(x, y) -> (uuidOf x, y))
uuidResultList = uuidConvert nodeResultList
uuidConsoleList = uuidConvert nodeConsoleList
in case getInstanceInfo uuidResultList inst of
Left err -> Left err
Right res -> Right (res, getConsoleInfo uuidConsoleList inst)
getAllConsoleParams :: ConfigData
-> [Instance]
-> ErrorResult [InstanceConsoleInfoParams]
getAllConsoleParams cfg = mapM $ \i ->
InstanceConsoleInfoParams i
<$> getPrimaryNode cfg i
<*> getPrimaryNodeGroup cfg i
<*> pure (getFilledInstHvParams [] cfg i)
<*> getFilledInstBeParams cfg i
compareParamsByNode :: InstanceConsoleInfoParams
-> InstanceConsoleInfoParams
-> Bool
compareParamsByNode x y = instConsInfoParamsNode x == instConsInfoParamsNode y
consoleParamsToCalls :: [InstanceConsoleInfoParams]
-> [(Node, RpcCallInstanceConsoleInfo)]
consoleParamsToCalls params =
let sortedParams = sortBy
(comparing (instPrimaryNode . instConsInfoParamsInstance)) params
groupedParams = groupBy compareParamsByNode sortedParams
in map (\x -> case x of
[] -> error "Programmer error: group must have one or more members"
paramGroup@(y:_) ->
let node = instConsInfoParamsNode y
packer z = do
name <- instName $ instConsInfoParamsInstance z
return (name, z)
in (node, RpcCallInstanceConsoleInfo . mapMaybe packer
$ paramGroup)
) groupedParams
getHypervisorSpecs :: ConfigData -> [Instance] -> [(Hypervisor, HvParams)]
getHypervisorSpecs cfg instances =
let hvs = nub . mapMaybe instHypervisor $ instances
hvParamMap = (fromContainer . clusterHvparams . configCluster $ cfg)
in zip hvs . map ((Map.!) hvParamMap) $ hvs
collectLiveData :: Bool
-> ConfigData
-> [String]
-> [Instance]
-> IO [(Instance, Runtime)]
collectLiveData liveDataEnabled cfg fields instances
| not liveDataEnabled = return . zip instances . repeat . Left .
RpcResultError $ "Live data disabled"
| otherwise = do
let hvSpecs = getHypervisorSpecs cfg instances
instanceNodes =
nub . justOk
$ map ( maybe (Bad $ ParameterError "no primary node") return
. instPrimaryNode
>=> getNode cfg) instances
goodNodes = nodesWithValidConfig cfg instanceNodes
instInfoRes <- executeRpcCall goodNodes (RpcCallAllInstancesInfo hvSpecs)
consInfoRes <-
if "console" `elem` fields
then case getAllConsoleParams cfg instances of
Ok p -> executeRpcCalls $ consoleParamsToCalls p
Bad _ -> return . zip goodNodes . repeat . Left $
RpcResultError "Cannot construct parameters for console info call"
else return []
return . zip instances .
map (extractLiveInfo instInfoRes consInfoRes) $ instances
getDiskTemplate :: ConfigData -> Instance -> ResultEntry
getDiskTemplate cfg inst =
let disks = getInstDisksFromObj cfg inst
getDt x = lidDiskType <$> diskLogicalId x
disk_types :: ErrorResult [DiskTemplate]
disk_types = nub <$> catMaybes <$> map getDt <$> disks
mix :: [DiskTemplate] -> J.JSValue
mix [] = J.showJSON C.dtDiskless
mix [t] = J.showJSON t
mix _ = J.showJSON C.dtMixed
in case mix <$> disk_types of
Ok t -> rsNormal t
Bad _ -> rsNoData