module Ganeti.DataCollectors.InstStatus
( main
, options
, arguments
, dcName
, dcVersion
, dcFormatVersion
, dcCategory
, dcKind
, dcReport
) where
import Control.Exception.Base
import qualified Data.ByteString.UTF8 as UTF8
import Data.List
import Data.Maybe
import qualified Data.Map as Map
import Network.BSD (getHostName)
import qualified Text.JSON as J
import Ganeti.BasicTypes as BT
import Ganeti.Confd.ClientFunctions
import Ganeti.Common
import qualified Ganeti.Constants as C
import Ganeti.DataCollectors.CLI
import Ganeti.DataCollectors.InstStatusTypes
import Ganeti.DataCollectors.Types
import Ganeti.Hypervisor.Xen
import Ganeti.Hypervisor.Xen.Types
import Ganeti.Logging
import Ganeti.Objects
import Ganeti.Path
import Ganeti.Types
import Ganeti.Utils
dcName :: String
dcName = C.dataCollectorInstStatus
dcVersion :: DCVersion
dcVersion = DCVerBuiltin
dcFormatVersion :: Int
dcFormatVersion = 1
dcCategory :: Maybe DCCategory
dcCategory = Just DCInstance
dcKind :: DCKind
dcKind = DCKStatus
dcReport :: IO DCReport
dcReport = buildInstStatusReport Nothing Nothing
options :: IO [OptType]
options = return
[ oConfdAddr
, oConfdPort
]
arguments :: [ArgCompletion]
arguments = []
getReasonTrail :: String -> IO ReasonTrail
getReasonTrail instanceName = do
fileName <- getInstReasonFilename instanceName
content <- try $ readFile fileName
case content of
Left e -> do
logWarning $
"Unable to open the reason trail for instance " ++ instanceName ++
" expected at " ++ fileName ++ ": " ++ show (e :: IOException)
return []
Right trailString ->
case J.decode trailString of
J.Ok t -> return t
J.Error msg -> do
logWarning $ "Unable to parse the reason trail: " ++ msg
return []
computeStatusField :: AdminState -> ActualState -> DCStatus
computeStatusField AdminDown actualState =
if actualState `notElem` [ActualShutdown, ActualDying]
then DCStatus DCSCBad "The instance is not stopped as it should be"
else DCStatus DCSCOk ""
computeStatusField AdminUp ActualHung =
DCStatus DCSCUnknown "Instance marked as running, but it appears to be hung"
computeStatusField AdminUp actualState =
if actualState `notElem` [ActualRunning, ActualBlocked]
then DCStatus DCSCBad "The instance is not running as it should be"
else DCStatus DCSCOk ""
computeStatusField AdminOffline _ =
DCStatus DCSCUnknown "The instance is marked as offline"
buildStatus :: Map.Map String Domain -> Map.Map Int UptimeInfo
-> RealInstanceData
-> IO InstStatus
buildStatus domains uptimes inst = do
let name = realInstName inst
currDomain = Map.lookup name domains
idNum = fmap domId currDomain
currUInfo = idNum >>= (`Map.lookup` uptimes)
uptime = fmap uInfoUptime currUInfo
adminState = realInstAdminState inst
actualState =
if adminState == AdminDown && isNothing currDomain
then ActualShutdown
else case currDomain of
(Just dom@(Domain _ _ _ _ (Just isHung))) ->
if isHung
then ActualHung
else domState dom
_ -> ActualUnknown
status = computeStatusField adminState actualState
trail <- getReasonTrail name
return $
InstStatus
name
(UTF8.toString $ realInstUuid inst)
adminState
actualState
uptime
(realInstMtime inst)
trail
status
computeGlobalStatus :: [InstStatus] -> DCStatus
computeGlobalStatus instStatusList =
let dcstatuses = map iStatStatus instStatusList
statuses = map (\s -> (dcStatusCode s, dcStatusMessage s)) dcstatuses
(code, strList) = foldr mergeStatuses (DCSCOk, [""]) statuses
in DCStatus code $ intercalate "\n" strList
buildInstStatusReport :: Maybe String -> Maybe Int -> IO DCReport
buildInstStatusReport srvAddr srvPort = do
node <- getHostName
answer <- runResultT $ getInstances node srvAddr srvPort
inst <- exitIfBad "Can't get instance info from ConfD" answer
d <- getInferredDomInfo
let toReal (RealInstance i) = Just i
toReal _ = Nothing
reportData <-
case d of
BT.Ok domains -> do
uptimes <- getUptimeInfo
let primaryInst = mapMaybe toReal $ fst inst
iStatus <- mapM (buildStatus domains uptimes) primaryInst
let globalStatus = computeGlobalStatus iStatus
return $ ReportData iStatus globalStatus
BT.Bad m ->
return . ReportData [] . DCStatus DCSCBad $
"Unable to receive the list of instances: " ++ m
let jsonReport = J.showJSON reportData
buildReport dcName dcVersion dcFormatVersion dcCategory dcKind jsonReport
main :: Options -> [String] -> IO ()
main opts _ = do
report <- buildInstStatusReport (optConfdAddr opts) (optConfdPort opts)
putStrLn $ J.encode report