]> git.ipfire.org Git - oddments/collecty.git/blame - src/collecty/plugins/sensors.py
graphs: Justify all legends
[oddments/collecty.git] / src / collecty / plugins / sensors.py
CommitLineData
f37913e8 1#!/usr/bin/python3
c389fccf
MT
2# encoding: utf-8
3###############################################################################
4# #
5# collecty - A system statistics collection daemon for IPFire #
6# Copyright (C) 2015 IPFire development team #
7# #
8# This program is free software: you can redistribute it and/or modify #
9# it under the terms of the GNU General Public License as published by #
10# the Free Software Foundation, either version 3 of the License, or #
11# (at your option) any later version. #
12# #
13# This program is distributed in the hope that it will be useful, #
14# but WITHOUT ANY WARRANTY; without even the implied warranty of #
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
16# GNU General Public License for more details. #
17# #
18# You should have received a copy of the GNU General Public License #
19# along with this program. If not, see <http://www.gnu.org/licenses/>. #
20# #
21###############################################################################
22
c389fccf
MT
23import os
24import re
25
a031204a 26from .. import _collecty
f37913e8 27from . import base
c389fccf 28
c389fccf
MT
29class GraphTemplateSensorsTemperature(base.GraphTemplate):
30 name = "sensors-temperature"
31
3311f8ad
MT
32 @property
33 def rrd_graph(self):
34 _ = self.locale.translate
35
36 return [
d5d4c0e7
MT
37 # Convert everything to Celsius
38 "CDEF:value_c=value,273.15,-",
39 "CDEF:critical_c=critical,273.15,-",
40 "CDEF:high_c=high,273.15,-",
41 "CDEF:low_c=low,273.15,-",
3311f8ad
MT
42
43 # Change colour when the value gets above high
d5d4c0e7
MT
44 "CDEF:value_c_high=value_c,high_c,GT,value_c,UNKN,IF",
45 "CDEF:value_c_normal=value_c,high_c,GT,UNKN,value_c,IF",
3311f8ad 46
3311f8ad 47 # Get data points for the threshold lines
d5d4c0e7
MT
48 "VDEF:critical_c_line=critical_c,MINIMUM",
49 "VDEF:low_c_line=low_c,MAXIMUM",
3311f8ad
MT
50
51 # Draw the temperature value
d5d4c0e7
MT
52 "LINE3:value_c_high#ff0000",
53 "LINE2:value_c_normal#00ff00:%-15s" % _("Temperature"),
3311f8ad
MT
54
55 # Draw the legend
d5d4c0e7 56 "GPRINT:value_c_cur:%10.2lf °C\l",
8ec1299b
MT
57 "GPRINT:value_c_avg: %-15s %%6.2lf °C" % _("Average"),
58 "GPRINT:value_c_max: %-15s %%6.2lf °C" % _("Maximum"),
59 "GPRINT:value_c_min: %-15s %%6.2lf °C" % _("Minimum"),
3311f8ad
MT
60
61 # Empty line
62 "COMMENT: \\n",
63
64 # Draw boundary lines
65 "COMMENT:%s\:" % _("Temperature Thresholds"),
d5d4c0e7
MT
66 "HRULE:critical_c_line#000000:%-15s" % _("Critical"),
67 "GPRINT:critical_c_line:%6.2lf °C\\r",
68 "HRULE:low_c_line#0000ff:%-15s" % _("Low"),
69 "GPRINT:low_c_line:%6.2lf °C\\r",
3311f8ad 70 ]
c389fccf
MT
71
72 @property
73 def graph_title(self):
3311f8ad 74 _ = self.locale.translate
c389fccf
MT
75 return _("Temperature (%s)") % self.object.sensor.name
76
77 @property
78 def graph_vertical_label(self):
3311f8ad 79 _ = self.locale.translate
c389fccf
MT
80 return _("° Celsius")
81
82
83class GraphTemplateSensorsProcessorTemperature(base.GraphTemplate):
84 name = "processor-temperature"
85
76c12e91
MT
86 core_colours = [
87 "#ff000033",
88 "#0000ff33",
89 "#00ff0033",
90 "#0000ff33",
91 ]
c389fccf
MT
92
93 def get_temperature_sensors(self):
12dd1b6f
MT
94 # Use the coretemp module if available
95 sensors = self.plugin.get_detected_sensor_objects("coretemp-*")
96
97 # Fall back to the ACPI sensor
98 if not sensors:
99 sensors = self.plugin.get_detected_sensor_objects("acpitz-virtual-*")
100
101 return sensors
c389fccf 102
76c12e91
MT
103 def get_objects(self, *args, **kwargs):
104 sensors = self.get_temperature_sensors()
c389fccf 105
76c12e91 106 return list(sensors)
c389fccf
MT
107
108 @property
109 def rrd_graph(self):
3311f8ad 110 _ = self.locale.translate
c389fccf
MT
111 rrd_graph = []
112
76c12e91
MT
113 counter = 0
114 ids = []
c389fccf 115
76c12e91
MT
116 for core in self.objects:
117 id = "core%s" % counter
118 ids.append(id)
119 counter += 1
c389fccf 120
76c12e91 121 rrd_graph += core.make_rrd_defs(id) + [
c389fccf 122 # Convert everything to celsius
76c12e91
MT
123 "CDEF:%s_value_c=%s_value,273.15,-" % (id, id),
124 "CDEF:%s_critical_c=%s_critical,273.15,-" % (id, id),
125 "CDEF:%s_high_c=%s_high,273.15,-" % (id, id),
c389fccf
MT
126 ]
127
d5d4c0e7
MT
128 # Compute the temperature of the processor
129 # by taking the average of all cores
76c12e91 130 all_core_values = ("%s_value_c" % id for id in ids)
c389fccf 131 rrd_graph += [
76c12e91 132 "CDEF:all_value_c=%s,%s,AVG" % (",".join(all_core_values), len(ids)),
d5d4c0e7 133 ]
c389fccf 134
d5d4c0e7
MT
135 # Get the high threshold of the first core
136 # (assuming that all cores have the same threshold)
137 for id in ids:
138 rrd_graph.append("CDEF:all_high_c=%s_high_c" % id)
139 break
c389fccf
MT
140
141 rrd_graph += [
c389fccf 142 # Change colour when the value gets above high
76c12e91
MT
143 "CDEF:all_value_c_high=all_value_c,all_high_c,GT,all_value_c,UNKN,IF",
144 "CDEF:all_value_c_normal=all_value_c,all_high_c,GT,UNKN,all_value_c,IF",
c389fccf 145
2b2d212d 146 "LINE2:all_value_c_high#FF0000",
8ec1299b 147 "LINE2:all_value_c_normal#000000:%-15s" % _("Temperature"),
c389fccf 148
8ec1299b
MT
149 "GPRINT:all_value_c_avg: %-15s %%6.2lf °C" % _("Average"),
150 "GPRINT:all_value_c_max: %-15s %%6.2lf °C" % _("Maximum"),
151 "GPRINT:all_value_c_min: %-15s %%6.2lf °C" % _("Minimum"),
c389fccf
MT
152 ]
153
76c12e91
MT
154 for id, core, colour in zip(ids, self.objects, self.core_colours):
155 rrd_graph += [
c389fccf
MT
156 # TODO these lines were supposed to be dashed, but that
157 # didn't really work here
2b2d212d 158 "LINE1:%s_value_c%s:%-10s" % (id, colour, core.sensor.label),
c389fccf
MT
159 ]
160
c389fccf 161 # Draw the critical line
76c12e91
MT
162 for id in ids:
163 rrd_graph += [
164 "HRULE:%s_critical_c_min#000000:%-15s" % (id, _("Critical")),
d5d4c0e7 165 "GPRINT:%s_critical_c_min:%%6.2lf °C\\r" % id,
76c12e91
MT
166 ]
167 break
c389fccf
MT
168
169 return rrd_graph
170
171 @property
172 def graph_title(self):
3311f8ad 173 _ = self.locale.translate
c389fccf
MT
174 return _("Processor")
175
176 @property
177 def graph_vertical_label(self):
3311f8ad 178 _ = self.locale.translate
c389fccf
MT
179 return _("Temperature")
180
181
182class SensorBaseObject(base.Object):
183 def init(self, sensor):
184 self.sensor = sensor
185
186 def __repr__(self):
187 return "<%s %s (%s)>" % (self.__class__.__name__, self.sensor.name, self.sensor.label)
188
189 @property
190 def id(self):
191 return "-".join((self.sensor.name, self.sensor.label))
192
193 @property
194 def type(self):
195 return self.sensor.type
196
197
198class SensorTemperatureObject(SensorBaseObject):
199 rrd_schema = [
200 "DS:value:GAUGE:0:U",
201 "DS:critical:GAUGE:0:U",
202 "DS:low:GAUGE:0:U",
203 "DS:high:GAUGE:0:U",
204 ]
205
206 def collect(self):
207 assert self.type == "temperature"
208
209 return (
210 self.sensor.value,
211 self.critical,
212 self.low,
213 self.high,
214 )
215
216 @property
217 def critical(self):
218 try:
219 return self.sensor.critical
220 except AttributeError:
221 return "NaN"
222
223 @property
224 def low(self):
225 try:
226 return self.sensor.minimum
227 except AttributeError:
228 return "NaN"
229
230 @property
231 def high(self):
232 try:
233 return self.sensor.high
234 except AttributeError:
235 return "NaN"
236
237
238class SensorVoltageObject(SensorBaseObject):
239 rrd_schema = [
240 "DS:value:GAUGE:0:U",
241 "DS:minimum:GAUGE:0:U",
242 "DS:maximum:GAUGE:0:U",
243 ]
244
245 def collect(self):
246 assert self.type == "voltage"
247
248 return (
249 self.sensor.value,
250 self.minimum,
251 self.maximum,
252 )
253
254 @property
255 def minimum(self):
256 try:
257 return self.sensor.minimum
258 except AttributeError:
259 return "NaN"
260
261 @property
262 def maximum(self):
263 try:
264 return self.sensor.maximum
265 except AttributeError:
266 return "NaN"
267
268
269class SensorFanObject(SensorBaseObject):
270 rrd_schema = [
271 "DS:value:GAUGE:0:U",
272 "DS:minimum:GAUGE:0:U",
273 "DS:maximum:GAUGE:0:U",
274 ]
275
276 def collect(self):
277 assert self.type == "fan"
278
279 return (
280 self.sensor.value,
281 self.minimum,
282 self.maximum,
283 )
284
285 @property
d0588abe 286 def minimum(self):
c389fccf
MT
287 try:
288 return self.sensor.minimum
289 except AttributeError:
290 return "NaN"
291
292 @property
293 def maximum(self):
294 try:
295 return self.sensor.maximum
296 except AttributeError:
297 return "NaN"
298
299
300class SensorsPlugin(base.Plugin):
301 name = "sensors"
302 description = "Sensors Plugin"
303
304 templates = [
305 GraphTemplateSensorsProcessorTemperature,
306 GraphTemplateSensorsTemperature,
307 ]
308
309 def init(self):
310 # Initialise the sensors library.
311 _collecty.sensors_init()
312
313 def __del__(self):
314 _collecty.sensors_cleanup()
315
316 @property
317 def objects(self):
318 return self.get_detected_sensor_objects()
319
320 def get_detected_sensor_objects(self, what=None):
321 for sensor in _collecty.get_detected_sensors(what):
322 if sensor.type == "temperature":
323 yield SensorTemperatureObject(self, sensor)
324
325 elif sensor.type == "voltage":
326 yield SensorVoltageObject(self, sensor)
327
328 elif sensor.type == "fan":
329 yield SensorFanObject(self, sensor)