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