]> git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/sensors.py
Add sensors plugin
[collecty.git] / src / collecty / plugins / sensors.py
1 #!/usr/bin/python
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 import base
28
29 from ..i18n import _
30
31 class GraphTemplateSensorsTemperature(base.GraphTemplate):
32 name = "sensors-temperature"
33
34 rrd_graph = [
35 "DEF:value_kelvin=%(file)s:value:AVERAGE",
36 "DEF:critical_kelvin=%(file)s:critical:AVERAGE",
37 "DEF:high_kelvin=%(file)s:high:AVERAGE",
38 "DEF:low_kelvin=%(file)s:low:AVERAGE",
39
40 # Convert everything to celsius
41 "CDEF:value=value_kelvin,273.15,-",
42 "CDEF:critical=critical_kelvin,273.15,-",
43 "CDEF:high=high_kelvin,273.15,-",
44 "CDEF:low=low_kelvin,273.15,-",
45
46 # Change colour when the value gets above high
47 "CDEF:value_high=value,high,GT,value,UNKN,IF",
48 "CDEF:value_normal=value,high,GT,UNKN,value,IF",
49
50 "VDEF:value_cur=value,LAST",
51 "VDEF:value_avg=value,AVERAGE",
52 "VDEF:value_max=value,MAXIMUM",
53 "VDEF:value_min=value,MINIMUM",
54
55 # Get data points for the threshold lines
56 "VDEF:critical_line=critical,MINIMUM",
57 "VDEF:low_line=low,MAXIMUM",
58
59 # Draw the temperature value
60 "LINE3:value_high#ff0000",
61 "LINE2:value_normal#00ff00:%-15s" % _("Temperature"),
62
63 # Draw the legend
64 "GPRINT:value_cur:%%10.2lf °C\l",
65 "GPRINT:value_avg: %-15s %%6.2lf °C\l" % _("Average"),
66 "GPRINT:value_max: %-15s %%6.2lf °C\l" % _("Maximum"),
67 "GPRINT:value_min: %-15s %%6.2lf °C\l" % _("Minimum"),
68
69 # Empty line
70 "COMMENT: \\n",
71
72 # Draw boundary lines
73 "COMMENT:%s\:" % _("Temperature Thresholds"),
74 "HRULE:critical_line#000000:%-15s" % _("Critical"),
75 "GPRINT:critical_line:%%6.2lf °C\\r",
76 "HRULE:low_line#0000ff:%-15s" % _("Low"),
77 "GPRINT:low_line:%%6.2lf °C\\r",
78 ]
79
80 @property
81 def graph_title(self):
82 return _("Temperature (%s)") % self.object.sensor.name
83
84 @property
85 def graph_vertical_label(self):
86 return _("° Celsius")
87
88
89 class GraphTemplateSensorsProcessorTemperature(base.GraphTemplate):
90 name = "processor-temperature"
91
92 core_colours = {
93 "core0" : "#ff000033",
94 "core1" : "#0000ff33",
95 "core2" : "#00ff0033",
96 "core3" : "#0000ff33",
97 }
98
99 def get_temperature_sensors(self):
100 return self.plugin.get_detected_sensor_objects("coretemp-*")
101
102 def get_object_table(self):
103 objects_table = {}
104
105 counter = 0
106 for object in self.get_temperature_sensors():
107 objects_table["core%s" % counter] = object
108 counter += 1
109
110 return objects_table
111
112 @property
113 def rrd_graph(self):
114 rrd_graph = []
115
116 cores = sorted(self.object_table.keys())
117
118 for core in cores:
119 i = {
120 "core" : core,
121 }
122
123 core_graph = [
124 "DEF:value_kelvin_%(core)s=%%(%(core)s)s:value:AVERAGE",
125 "DEF:critical_kelvin_%(core)s=%%(%(core)s)s:critical:AVERAGE",
126 "DEF:high_kelvin_%(core)s=%%(%(core)s)s:high:AVERAGE",
127
128 # Convert everything to celsius
129 "CDEF:value_%(core)s=value_kelvin_%(core)s,273.15,-",
130 "CDEF:critical_%(core)s=critical_kelvin_%(core)s,273.15,-",
131 "CDEF:high_%(core)s=high_kelvin_%(core)s,273.15,-",
132 ]
133
134 rrd_graph += [line % i for line in core_graph]
135
136 all_core_values = ("value_%s" % c for c in cores)
137 all_core_highs = ("high_%s" % c for c in cores)
138
139 rrd_graph += [
140 # Compute the temperature of the processor
141 # by taking the average of all cores
142 "CDEF:value=%s,%s,AVG" % (",".join(all_core_values), len(cores)),
143 "CDEF:high=%s,MIN" % ",".join(all_core_highs),
144
145 # Change colour when the value gets above high
146 "CDEF:value_high=value,high,GT,value,UNKN,IF",
147 "CDEF:value_normal=value,high,GT,UNKN,value,IF",
148
149 "VDEF:value_avg=value,AVERAGE",
150 "VDEF:value_max=value,MAXIMUM",
151 "VDEF:value_min=value,MINIMUM",
152
153 "LINE3:value_high#FF0000",
154 "LINE3:value_normal#000000:%-15s\l" % _("Temperature"),
155
156 "GPRINT:value_avg: %-15s %%6.2lf °C\l" % _("Average"),
157 "GPRINT:value_max: %-15s %%6.2lf °C\l" % _("Maximum"),
158 "GPRINT:value_min: %-15s %%6.2lf °C\l" % _("Minimum"),
159 ]
160
161 for core in cores:
162 object = self.object_table.get(core)
163
164 i = {
165 "colour" : self.core_colours.get(core, "#000000"),
166 "core" : core,
167 "label" : object.sensor.label,
168 }
169
170 core_graph = [
171 # TODO these lines were supposed to be dashed, but that
172 # didn't really work here
173 "LINE2:value_%(core)s%(colour)s:%(label)-10s",
174 ]
175
176 rrd_graph += [line % i for line in core_graph]
177
178 # Draw the critical line
179 rrd_graph += [
180 "VDEF:critical_line=critical_core0,MINIMUM",
181 "HRULE:critical_line#000000:%-15s" % _("Critical"),
182 "GPRINT:critical_line:%%6.2lf °C\\r",
183 ]
184
185 return rrd_graph
186
187 @property
188 def graph_title(self):
189 return _("Processor")
190
191 @property
192 def graph_vertical_label(self):
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 mimimum(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)