1 {-| Instance status data collector.
7 Copyright (C) 2013 Google Inc.
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.
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.
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
26 module Ganeti.DataCollectors.InstStatus
39 import Control.Exception.Base
42 import qualified Data.Map as Map
43 import Network.BSD (getHostName)
44 import qualified Text.JSON as J
46 import qualified Ganeti.BasicTypes as BT
47 import Ganeti.Confd.Client
48 import Ganeti.Confd.Types
50 import Ganeti.DataCollectors.CLI
51 import Ganeti.DataCollectors.InstStatusTypes
52 import Ganeti.DataCollectors.Types
53 import Ganeti.Hypervisor.Xen
54 import Ganeti.Hypervisor.Xen.Types
62 -- | The name of this data collector.
64 dcName = "inst-status-xen"
66 -- | The version of this data collector.
67 dcVersion :: DCVersion
68 dcVersion = DCVerBuiltin
70 -- | The version number for the data format of this data collector.
71 dcFormatVersion :: Int
74 -- | The category of this data collector.
75 dcCategory :: Maybe DCCategory
76 dcCategory = Just DCInstance
78 -- | The kind of this data collector.
82 -- | The report of this data collector.
83 dcReport :: IO DCReport
84 dcReport = buildInstStatusReport Nothing Nothing
86 -- * Command line options
88 options :: IO [OptType]
94 -- | The list of arguments supported by the program.
95 arguments :: [ArgCompletion]
98 -- | Get the list of instances ([primary], [secondary]) on the given node.
99 -- Implemented as a function, even if used a single time, to specify in a
100 -- convenient and elegant way the return data type, required in order to
101 -- prevent incurring in the monomorphism restriction.
102 -- The server address and the server port parameters are mainly intended
103 -- for testing purposes. If they are Nothing, the default values will be used.
108 -> IO (BT.Result ([Ganeti.Objects.Instance], [Ganeti.Objects.Instance]))
109 getInstances node srvAddr srvPort = do
110 client <- getConfdClient srvAddr srvPort
111 reply <- query client ReqNodeInstances $ PlainQuery node
113 case fmap (J.readJSON . confdReplyAnswer) reply of
114 Just (J.Ok instances) -> BT.Ok instances
115 Just (J.Error msg) -> BT.Bad msg
116 Nothing -> BT.Bad "No answer from the Confd server"
118 -- | Try to get the reason trail for an instance. In case it is not possible,
119 -- log the failure and return an empty list instead.
120 getReasonTrail :: String -> IO ReasonTrail
121 getReasonTrail instanceName = do
122 fileName <- getInstReasonFilename instanceName
123 content <- try $ readFile fileName
127 "Unable to open the reason trail for instance " ++ instanceName ++
128 " expected at " ++ fileName ++ ": " ++ show (e :: IOException)
131 case J.decode trailString of
134 logWarning $ "Unable to parse the reason trail: " ++ msg
137 -- | Determine the value of the status field for the report of one instance
138 computeStatusField :: AdminState -> ActualState -> DCStatus
139 computeStatusField AdminDown actualState =
140 if actualState `notElem` [ActualShutdown, ActualDying]
141 then DCStatus DCSCBad "The instance is not stopped as it should be"
142 else DCStatus DCSCOk ""
143 computeStatusField AdminUp ActualHung =
144 DCStatus DCSCUnknown "Instance marked as running, but it appears to be hung"
145 computeStatusField AdminUp actualState =
146 if actualState `notElem` [ActualRunning, ActualBlocked]
147 then DCStatus DCSCBad "The instance is not running as it should be"
148 else DCStatus DCSCOk ""
149 computeStatusField AdminOffline _ =
150 -- FIXME: The "offline" status seems not to be used anywhere in the source
151 -- code, but it is defined, so we have to consider it anyway here.
152 DCStatus DCSCUnknown "The instance is marked as offline"
154 -- Builds the status of an instance using runtime information about the Xen
155 -- Domains, their uptime information and the static information provided by
157 buildStatus :: Map.Map String Domain -> Map.Map Int UptimeInfo -> Instance
159 buildStatus domains uptimes inst = do
160 let name = instName inst
161 currDomain = Map.lookup name domains
162 idNum = fmap domId currDomain
163 currUInfo = idNum >>= (`Map.lookup` uptimes)
164 uptime = fmap uInfoUptime currUInfo
165 adminState = instAdminState inst
167 if adminState == AdminDown && isNothing currDomain
169 else case currDomain of
170 (Just dom@(Domain _ _ _ _ (Just isHung))) ->
175 status = computeStatusField adminState actualState
176 trail <- getReasonTrail name
188 -- | Compute the status code and message, given the current DRBD data
189 -- The final state will have the code corresponding to the worst code of
190 -- all the devices, and the error message given from the concatenation of the
191 -- non-empty error messages.
192 computeGlobalStatus :: [InstStatus] -> DCStatus
193 computeGlobalStatus instStatusList =
194 let dcstatuses = map iStatStatus instStatusList
195 statuses = map (\s -> (dcStatusCode s, dcStatusMessage s)) dcstatuses
196 (code, strList) = foldr mergeStatuses (DCSCOk, [""]) statuses
197 in DCStatus code $ intercalate "\n" strList
199 -- | Build the report of this data collector, containing all the information
200 -- about the status of the instances.
201 buildInstStatusReport :: Maybe String -> Maybe Int -> IO DCReport
202 buildInstStatusReport srvAddr srvPort = do
204 answer <- getInstances node srvAddr srvPort
205 inst <- exitIfBad "Can't get instance info from ConfD" answer
206 domains <- getInferredDomInfo
207 uptimes <- getUptimeInfo
208 let primaryInst = fst inst
209 iStatus <- mapM (buildStatus domains uptimes) primaryInst
210 let globalStatus = computeGlobalStatus iStatus
211 jsonReport = J.showJSON $ ReportData iStatus globalStatus
212 buildReport dcName dcVersion dcFormatVersion dcCategory dcKind jsonReport
215 main :: Options -> [String] -> IO ()
217 report <- buildInstStatusReport (optConfdAddr opts) (optConfdPort opts)
218 putStrLn $ J.encode report