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