root / src / Ganeti / DataCollectors / CPUload.hs @ 07e68848
History | View | Annotate | Download (5.8 kB)
1 | 2da679f7 | Spyros Trigazis | {-| @/proc/stat@ data collector. |
---|---|---|---|
2 | 2da679f7 | Spyros Trigazis | |
3 | 2da679f7 | Spyros Trigazis | -} |
4 | 2da679f7 | Spyros Trigazis | |
5 | 2da679f7 | Spyros Trigazis | {- |
6 | 2da679f7 | Spyros Trigazis | |
7 | 2da679f7 | Spyros Trigazis | Copyright (C) 2013 Google Inc. |
8 | 2da679f7 | Spyros Trigazis | |
9 | 2da679f7 | Spyros Trigazis | This program is free software; you can redistribute it and/or modify |
10 | 2da679f7 | Spyros Trigazis | it under the terms of the GNU General Public License as published by |
11 | 2da679f7 | Spyros Trigazis | the Free Software Foundation; either version 2 of the License, or |
12 | 2da679f7 | Spyros Trigazis | (at your option) any later version. |
13 | 2da679f7 | Spyros Trigazis | |
14 | 2da679f7 | Spyros Trigazis | This program is distributed in the hope that it will be useful, but |
15 | 2da679f7 | Spyros Trigazis | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | 2da679f7 | Spyros Trigazis | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | 2da679f7 | Spyros Trigazis | General Public License for more details. |
18 | 2da679f7 | Spyros Trigazis | |
19 | 2da679f7 | Spyros Trigazis | You should have received a copy of the GNU General Public License |
20 | 2da679f7 | Spyros Trigazis | along with this program; if not, write to the Free Software |
21 | 2da679f7 | Spyros Trigazis | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
22 | 2da679f7 | Spyros Trigazis | 02110-1301, USA. |
23 | 2da679f7 | Spyros Trigazis | |
24 | 2da679f7 | Spyros Trigazis | -} |
25 | 2da679f7 | Spyros Trigazis | |
26 | 2da679f7 | Spyros Trigazis | module Ganeti.DataCollectors.CPUload |
27 | 2da679f7 | Spyros Trigazis | ( dcName |
28 | 2da679f7 | Spyros Trigazis | , dcVersion |
29 | 2da679f7 | Spyros Trigazis | , dcFormatVersion |
30 | 2da679f7 | Spyros Trigazis | , dcCategory |
31 | 2da679f7 | Spyros Trigazis | , dcKind |
32 | 2da679f7 | Spyros Trigazis | , dcReport |
33 | 2da679f7 | Spyros Trigazis | , dcUpdate |
34 | 2da679f7 | Spyros Trigazis | ) where |
35 | 2da679f7 | Spyros Trigazis | |
36 | 2da679f7 | Spyros Trigazis | import qualified Control.Exception as E |
37 | 2da679f7 | Spyros Trigazis | import Data.Attoparsec.Text.Lazy as A |
38 | 2da679f7 | Spyros Trigazis | import Data.Text.Lazy (pack, unpack) |
39 | 2da679f7 | Spyros Trigazis | import qualified Text.JSON as J |
40 | 2da679f7 | Spyros Trigazis | import qualified Data.Sequence as Seq |
41 | 798582cf | Spyros Trigazis | import System.Posix.Unistd (getSysVar, SysVar(ClockTick)) |
42 | 2da679f7 | Spyros Trigazis | |
43 | 2da679f7 | Spyros Trigazis | import qualified Ganeti.BasicTypes as BT |
44 | 2da679f7 | Spyros Trigazis | import qualified Ganeti.Constants as C |
45 | 2da679f7 | Spyros Trigazis | import Ganeti.Cpu.LoadParser(cpustatParser) |
46 | 2da679f7 | Spyros Trigazis | import Ganeti.DataCollectors.Types |
47 | 2da679f7 | Spyros Trigazis | import Ganeti.Utils |
48 | 2da679f7 | Spyros Trigazis | import Ganeti.Cpu.Types |
49 | 2da679f7 | Spyros Trigazis | |
50 | 2da679f7 | Spyros Trigazis | -- | The default path of the CPU status file. |
51 | 2da679f7 | Spyros Trigazis | -- It is hardcoded because it is not likely to change. |
52 | 2da679f7 | Spyros Trigazis | defaultFile :: FilePath |
53 | 2da679f7 | Spyros Trigazis | defaultFile = C.statFile |
54 | 2da679f7 | Spyros Trigazis | |
55 | 2da679f7 | Spyros Trigazis | -- | The buffer size of the values kept in the map. |
56 | 2da679f7 | Spyros Trigazis | bufferSize :: Int |
57 | 2da679f7 | Spyros Trigazis | bufferSize = C.cpuavgloadBufferSize |
58 | 2da679f7 | Spyros Trigazis | |
59 | 2da679f7 | Spyros Trigazis | -- | The window size of the values that will export the average load. |
60 | 2da679f7 | Spyros Trigazis | windowSize :: Integer |
61 | 2da679f7 | Spyros Trigazis | windowSize = toInteger C.cpuavgloadWindowSize |
62 | 2da679f7 | Spyros Trigazis | |
63 | 2da679f7 | Spyros Trigazis | -- | The default setting for the maximum amount of not parsed character to |
64 | 2da679f7 | Spyros Trigazis | -- print in case of error. |
65 | 2da679f7 | Spyros Trigazis | -- It is set to use most of the screen estate on a standard 80x25 terminal. |
66 | 2da679f7 | Spyros Trigazis | -- TODO: add the possibility to set this with a command line parameter. |
67 | 2da679f7 | Spyros Trigazis | defaultCharNum :: Int |
68 | 2da679f7 | Spyros Trigazis | defaultCharNum = 80*20 |
69 | 2da679f7 | Spyros Trigazis | |
70 | 2da679f7 | Spyros Trigazis | -- | The name of this data collector. |
71 | 2da679f7 | Spyros Trigazis | dcName :: String |
72 | 2da679f7 | Spyros Trigazis | dcName = "cpu-avg-load" |
73 | 2da679f7 | Spyros Trigazis | |
74 | 2da679f7 | Spyros Trigazis | -- | The version of this data collector. |
75 | 2da679f7 | Spyros Trigazis | dcVersion :: DCVersion |
76 | 2da679f7 | Spyros Trigazis | dcVersion = DCVerBuiltin |
77 | 2da679f7 | Spyros Trigazis | |
78 | 2da679f7 | Spyros Trigazis | -- | The version number for the data format of this data collector. |
79 | 2da679f7 | Spyros Trigazis | dcFormatVersion :: Int |
80 | 2da679f7 | Spyros Trigazis | dcFormatVersion = 1 |
81 | 2da679f7 | Spyros Trigazis | |
82 | 2da679f7 | Spyros Trigazis | -- | The category of this data collector. |
83 | 2da679f7 | Spyros Trigazis | dcCategory :: Maybe DCCategory |
84 | 2da679f7 | Spyros Trigazis | dcCategory = Nothing |
85 | 2da679f7 | Spyros Trigazis | |
86 | 2da679f7 | Spyros Trigazis | -- | The kind of this data collector. |
87 | 2da679f7 | Spyros Trigazis | dcKind :: DCKind |
88 | 2da679f7 | Spyros Trigazis | dcKind = DCKPerf |
89 | 2da679f7 | Spyros Trigazis | |
90 | 2da679f7 | Spyros Trigazis | -- | The data exported by the data collector, taken from the default location. |
91 | 2da679f7 | Spyros Trigazis | dcReport :: Maybe CollectorData -> IO DCReport |
92 | 2da679f7 | Spyros Trigazis | dcReport colData = |
93 | 2da679f7 | Spyros Trigazis | let cpuLoadData = |
94 | 2da679f7 | Spyros Trigazis | case colData of |
95 | 2da679f7 | Spyros Trigazis | Nothing -> Seq.empty |
96 | 2da679f7 | Spyros Trigazis | Just colData' -> |
97 | 2da679f7 | Spyros Trigazis | case colData' of |
98 | 2da679f7 | Spyros Trigazis | CPULoadData v -> v |
99 | 2da679f7 | Spyros Trigazis | in buildDCReport cpuLoadData |
100 | 2da679f7 | Spyros Trigazis | -- | Data stored by the collector in mond's memory. |
101 | 2da679f7 | Spyros Trigazis | type Buffer = Seq.Seq (Integer, [Int]) |
102 | 2da679f7 | Spyros Trigazis | |
103 | 2da679f7 | Spyros Trigazis | -- | Compute the load from a CPU. |
104 | 2da679f7 | Spyros Trigazis | computeLoad :: CPUstat -> Int |
105 | 2da679f7 | Spyros Trigazis | computeLoad cpuData = |
106 | 2da679f7 | Spyros Trigazis | csUser cpuData + csNice cpuData + csSystem cpuData |
107 | 2da679f7 | Spyros Trigazis | + csIowait cpuData + csIrq cpuData + csSoftirq cpuData |
108 | 2da679f7 | Spyros Trigazis | + csSteal cpuData + csGuest cpuData + csGuestNice cpuData |
109 | 2da679f7 | Spyros Trigazis | |
110 | 2da679f7 | Spyros Trigazis | -- | Reads and Computes the load for each CPU. |
111 | 2da679f7 | Spyros Trigazis | dcCollectFromFile :: FilePath -> IO (Integer, [Int]) |
112 | 2da679f7 | Spyros Trigazis | dcCollectFromFile inputFile = do |
113 | 2da679f7 | Spyros Trigazis | contents <- |
114 | 2da679f7 | Spyros Trigazis | ((E.try $ readFile inputFile) :: IO (Either IOError String)) >>= |
115 | 2da679f7 | Spyros Trigazis | exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok |
116 | 2da679f7 | Spyros Trigazis | cpustatData <- |
117 | 2da679f7 | Spyros Trigazis | case A.parse cpustatParser $ pack contents of |
118 | 2da679f7 | Spyros Trigazis | A.Fail unparsedText contexts errorMessage -> exitErr $ |
119 | 2da679f7 | Spyros Trigazis | show (Prelude.take defaultCharNum $ unpack unparsedText) ++ "\n" |
120 | 2da679f7 | Spyros Trigazis | ++ show contexts ++ "\n" ++ errorMessage |
121 | 2da679f7 | Spyros Trigazis | A.Done _ cpustatD -> return cpustatD |
122 | 2da679f7 | Spyros Trigazis | now <- getCurrentTime |
123 | 2da679f7 | Spyros Trigazis | let timestamp = now :: Integer |
124 | 2da679f7 | Spyros Trigazis | return (timestamp, map computeLoad cpustatData) |
125 | 2da679f7 | Spyros Trigazis | |
126 | 2da679f7 | Spyros Trigazis | -- | Returns the collected data in the appropriate type. |
127 | 2da679f7 | Spyros Trigazis | dcCollect :: IO Buffer |
128 | 2da679f7 | Spyros Trigazis | dcCollect = do |
129 | 2da679f7 | Spyros Trigazis | l <- dcCollectFromFile defaultFile |
130 | 2da679f7 | Spyros Trigazis | return (Seq.singleton l) |
131 | 2da679f7 | Spyros Trigazis | |
132 | 2da679f7 | Spyros Trigazis | -- | Formats data for JSON transformation. |
133 | 2da679f7 | Spyros Trigazis | formatData :: [Double] -> CPUavgload |
134 | 2da679f7 | Spyros Trigazis | formatData [] = CPUavgload (0 :: Int) [] (0 :: Double) |
135 | 2da679f7 | Spyros Trigazis | formatData l@(x:xs) = CPUavgload (length l - 1) xs x |
136 | 2da679f7 | Spyros Trigazis | |
137 | 2da679f7 | Spyros Trigazis | -- | Update a Map Entry. |
138 | 2da679f7 | Spyros Trigazis | updateEntry :: Buffer -> Buffer -> Buffer |
139 | 2da679f7 | Spyros Trigazis | updateEntry newBuffer mapEntry = |
140 | 2da679f7 | Spyros Trigazis | (Seq.><) newBuffer |
141 | 2da679f7 | Spyros Trigazis | (if Seq.length mapEntry < bufferSize |
142 | 2da679f7 | Spyros Trigazis | then mapEntry |
143 | 2da679f7 | Spyros Trigazis | else Seq.drop 1 mapEntry) |
144 | 2da679f7 | Spyros Trigazis | |
145 | 2da679f7 | Spyros Trigazis | -- | Updates the given Collector data. |
146 | 2da679f7 | Spyros Trigazis | dcUpdate :: Maybe CollectorData -> IO CollectorData |
147 | 2da679f7 | Spyros Trigazis | dcUpdate mcd = do |
148 | 2da679f7 | Spyros Trigazis | v <- dcCollect |
149 | 2da679f7 | Spyros Trigazis | let new_v = |
150 | 2da679f7 | Spyros Trigazis | case mcd of |
151 | 2da679f7 | Spyros Trigazis | Nothing -> v |
152 | 2da679f7 | Spyros Trigazis | Just cd -> |
153 | 2da679f7 | Spyros Trigazis | case cd of |
154 | 2da679f7 | Spyros Trigazis | CPULoadData old_v -> updateEntry v old_v |
155 | 2da679f7 | Spyros Trigazis | new_v `seq` return $ CPULoadData new_v |
156 | 2da679f7 | Spyros Trigazis | |
157 | 2da679f7 | Spyros Trigazis | -- | Computes the average load for every CPU and the overall from data read |
158 | 2da679f7 | Spyros Trigazis | -- from the map. |
159 | 798582cf | Spyros Trigazis | computeAverage :: Buffer -> Integer -> Integer -> [Double] |
160 | 798582cf | Spyros Trigazis | computeAverage s w ticks = |
161 | 2da679f7 | Spyros Trigazis | let window = Seq.takeWhileL ((> w) . fst) s |
162 | 2da679f7 | Spyros Trigazis | go Seq.EmptyL _ = [] |
163 | 2da679f7 | Spyros Trigazis | go _ Seq.EmptyR = [] |
164 | 2da679f7 | Spyros Trigazis | go (leftmost Seq.:< _) (_ Seq.:> rightmost) = do |
165 | 2da679f7 | Spyros Trigazis | let (timestampL, listL) = leftmost |
166 | 2da679f7 | Spyros Trigazis | (timestampR, listR) = rightmost |
167 | 2da679f7 | Spyros Trigazis | work = zipWith (-) listL listR |
168 | 798582cf | Spyros Trigazis | overall = (timestampL - timestampR) * ticks |
169 | 2da679f7 | Spyros Trigazis | map (\x -> fromIntegral x / fromIntegral overall) work |
170 | 2da679f7 | Spyros Trigazis | in go (Seq.viewl window) (Seq.viewr window) |
171 | 2da679f7 | Spyros Trigazis | |
172 | 2da679f7 | Spyros Trigazis | -- | This function computes the JSON representation of the CPU load. |
173 | 2da679f7 | Spyros Trigazis | buildJsonReport :: Buffer -> IO J.JSValue |
174 | 798582cf | Spyros Trigazis | buildJsonReport v = do |
175 | 798582cf | Spyros Trigazis | ticks <- getSysVar ClockTick |
176 | 798582cf | Spyros Trigazis | let res = computeAverage v windowSize ticks |
177 | 798582cf | Spyros Trigazis | return . J.showJSON $ formatData res |
178 | 2da679f7 | Spyros Trigazis | |
179 | 2da679f7 | Spyros Trigazis | -- | This function computes the DCReport for the CPU load. |
180 | 2da679f7 | Spyros Trigazis | buildDCReport :: Buffer -> IO DCReport |
181 | 2da679f7 | Spyros Trigazis | buildDCReport v = |
182 | 2da679f7 | Spyros Trigazis | buildJsonReport v >>= |
183 | 2da679f7 | Spyros Trigazis | buildReport dcName dcVersion dcFormatVersion dcCategory dcKind |