Encode UUIDs as ByteStrings
[ganeti-github.git] / src / Ganeti / DataCollectors / InstStatus.hs
1 {-| Instance status data collector.
2
3 -}
4
5 {-
6
7 Copyright (C) 2013 Google Inc.
8 All rights reserved.
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are
12 met:
13
14 1. Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
25 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 -}
34
35 module Ganeti.DataCollectors.InstStatus
36 ( main
37 , options
38 , arguments
39 , dcName
40 , dcVersion
41 , dcFormatVersion
42 , dcCategory
43 , dcKind
44 , dcReport
45 ) where
46
47
48 import Control.Exception.Base
49 import qualified Data.ByteString.UTF8 as UTF8
50 import Data.List
51 import Data.Maybe
52 import qualified Data.Map as Map
53 import Network.BSD (getHostName)
54 import qualified Text.JSON as J
55
56 import Ganeti.BasicTypes as BT
57 import Ganeti.Confd.ClientFunctions
58 import Ganeti.Common
59 import qualified Ganeti.Constants as C
60 import Ganeti.DataCollectors.CLI
61 import Ganeti.DataCollectors.InstStatusTypes
62 import Ganeti.DataCollectors.Types
63 import Ganeti.Hypervisor.Xen
64 import Ganeti.Hypervisor.Xen.Types
65 import Ganeti.Logging
66 import Ganeti.Objects
67 import Ganeti.Path
68 import Ganeti.Types
69 import Ganeti.Utils
70
71
72 -- | The name of this data collector.
73 dcName :: String
74 dcName = C.dataCollectorInstStatus
75
76 -- | The version of this data collector.
77 dcVersion :: DCVersion
78 dcVersion = DCVerBuiltin
79
80 -- | The version number for the data format of this data collector.
81 dcFormatVersion :: Int
82 dcFormatVersion = 1
83
84 -- | The category of this data collector.
85 dcCategory :: Maybe DCCategory
86 dcCategory = Just DCInstance
87
88 -- | The kind of this data collector.
89 dcKind :: DCKind
90 dcKind = DCKStatus
91
92 -- | The report of this data collector.
93 dcReport :: IO DCReport
94 dcReport = buildInstStatusReport Nothing Nothing
95
96 -- * Command line options
97
98 options :: IO [OptType]
99 options = return
100 [ oConfdAddr
101 , oConfdPort
102 ]
103
104 -- | The list of arguments supported by the program.
105 arguments :: [ArgCompletion]
106 arguments = []
107
108 -- | Try to get the reason trail for an instance. In case it is not possible,
109 -- log the failure and return an empty list instead.
110 getReasonTrail :: String -> IO ReasonTrail
111 getReasonTrail instanceName = do
112 fileName <- getInstReasonFilename instanceName
113 content <- try $ readFile fileName
114 case content of
115 Left e -> do
116 logWarning $
117 "Unable to open the reason trail for instance " ++ instanceName ++
118 " expected at " ++ fileName ++ ": " ++ show (e :: IOException)
119 return []
120 Right trailString ->
121 case J.decode trailString of
122 J.Ok t -> return t
123 J.Error msg -> do
124 logWarning $ "Unable to parse the reason trail: " ++ msg
125 return []
126
127 -- | Determine the value of the status field for the report of one instance
128 computeStatusField :: AdminState -> ActualState -> DCStatus
129 computeStatusField AdminDown actualState =
130 if actualState `notElem` [ActualShutdown, ActualDying]
131 then DCStatus DCSCBad "The instance is not stopped as it should be"
132 else DCStatus DCSCOk ""
133 computeStatusField AdminUp ActualHung =
134 DCStatus DCSCUnknown "Instance marked as running, but it appears to be hung"
135 computeStatusField AdminUp actualState =
136 if actualState `notElem` [ActualRunning, ActualBlocked]
137 then DCStatus DCSCBad "The instance is not running as it should be"
138 else DCStatus DCSCOk ""
139 computeStatusField AdminOffline _ =
140 -- FIXME: The "offline" status seems not to be used anywhere in the source
141 -- code, but it is defined, so we have to consider it anyway here.
142 DCStatus DCSCUnknown "The instance is marked as offline"
143
144 -- Builds the status of an instance using runtime information about the Xen
145 -- Domains, their uptime information and the static information provided by
146 -- the ConfD server.
147 buildStatus :: Map.Map String Domain -> Map.Map Int UptimeInfo
148 -> RealInstanceData
149 -> IO InstStatus
150 buildStatus domains uptimes inst = do
151 let name = realInstName inst
152 currDomain = Map.lookup name domains
153 idNum = fmap domId currDomain
154 currUInfo = idNum >>= (`Map.lookup` uptimes)
155 uptime = fmap uInfoUptime currUInfo
156 adminState = realInstAdminState inst
157 actualState =
158 if adminState == AdminDown && isNothing currDomain
159 then ActualShutdown
160 else case currDomain of
161 (Just dom@(Domain _ _ _ _ (Just isHung))) ->
162 if isHung
163 then ActualHung
164 else domState dom
165 _ -> ActualUnknown
166 status = computeStatusField adminState actualState
167 trail <- getReasonTrail name
168 return $
169 InstStatus
170 name
171 (UTF8.toString $ realInstUuid inst)
172 adminState
173 actualState
174 uptime
175 (realInstMtime inst)
176 trail
177 status
178
179 -- | Compute the status code and message, given the current DRBD data
180 -- The final state will have the code corresponding to the worst code of
181 -- all the devices, and the error message given from the concatenation of the
182 -- non-empty error messages.
183 computeGlobalStatus :: [InstStatus] -> DCStatus
184 computeGlobalStatus instStatusList =
185 let dcstatuses = map iStatStatus instStatusList
186 statuses = map (\s -> (dcStatusCode s, dcStatusMessage s)) dcstatuses
187 (code, strList) = foldr mergeStatuses (DCSCOk, [""]) statuses
188 in DCStatus code $ intercalate "\n" strList
189
190 -- | Build the report of this data collector, containing all the information
191 -- about the status of the instances.
192 buildInstStatusReport :: Maybe String -> Maybe Int -> IO DCReport
193 buildInstStatusReport srvAddr srvPort = do
194 node <- getHostName
195 answer <- runResultT $ getInstances node srvAddr srvPort
196 inst <- exitIfBad "Can't get instance info from ConfD" answer
197 d <- getInferredDomInfo
198 let toReal (RealInstance i) = Just i
199 toReal _ = Nothing
200 reportData <-
201 case d of
202 BT.Ok domains -> do
203 uptimes <- getUptimeInfo
204 let primaryInst = mapMaybe toReal $ fst inst
205 iStatus <- mapM (buildStatus domains uptimes) primaryInst
206 let globalStatus = computeGlobalStatus iStatus
207 return $ ReportData iStatus globalStatus
208 BT.Bad m ->
209 return . ReportData [] . DCStatus DCSCBad $
210 "Unable to receive the list of instances: " ++ m
211 let jsonReport = J.showJSON reportData
212 buildReport dcName dcVersion dcFormatVersion dcCategory dcKind jsonReport
213
214 -- | Main function.
215 main :: Options -> [String] -> IO ()
216 main opts _ = do
217 report <- buildInstStatusReport (optConfdAddr opts) (optConfdPort opts)
218 putStrLn $ J.encode report