]> git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/sensors.py
Refectoring of the main classes
[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 "#ff000033",
95 "#0000ff33",
96 "#00ff0033",
97 "#0000ff33",
98 ]
99
100 def get_temperature_sensors(self):
101 return self.plugin.get_detected_sensor_objects("coretemp-*")
102
103 def get_objects(self, *args, **kwargs):
104 sensors = self.get_temperature_sensors()
105
106 return list(sensors)
107
108 @property
109 def rrd_graph(self):
110 _ = self.locale.translate
111 rrd_graph = []
112
113 counter = 0
114 ids = []
115
116 for core in self.objects:
117 id = "core%s" % counter
118 ids.append(id)
119 counter += 1
120
121 rrd_graph += core.make_rrd_defs(id) + [
122 # Convert everything to celsius
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),
126 ]
127
128 all_core_values = ("%s_value_c" % id for id in ids)
129 all_core_highs = ("%s_high_c" % id for id in ids)
130
131 rrd_graph += [
132 # Compute the temperature of the processor
133 # by taking the average of all cores
134 "CDEF:all_value_c=%s,%s,AVG" % (",".join(all_core_values), len(ids)),
135 "CDEF:all_high_c=%s,MIN" % ",".join(all_core_highs),
136
137 # Change colour when the value gets above high
138 "CDEF:all_value_c_high=all_value_c,all_high_c,GT,all_value_c,UNKN,IF",
139 "CDEF:all_value_c_normal=all_value_c,all_high_c,GT,UNKN,all_value_c,IF",
140
141 "LINE3:all_value_c_high#FF0000",
142 "LINE3:all_value_c_normal#000000:%-15s\l" % _("Temperature"),
143
144 "GPRINT:all_value_c_avg: %-15s %%6.2lf °C\l" % _("Average"),
145 "GPRINT:all_value_c_max: %-15s %%6.2lf °C\l" % _("Maximum"),
146 "GPRINT:all_value_c_min: %-15s %%6.2lf °C\l" % _("Minimum"),
147 ]
148
149 for id, core, colour in zip(ids, self.objects, self.core_colours):
150 rrd_graph += [
151 # TODO these lines were supposed to be dashed, but that
152 # didn't really work here
153 "LINE2:%s_value_c%s:%-10s" % (id, colour, core.sensor.label),
154 ]
155
156 # Draw the critical line
157 for id in ids:
158 rrd_graph += [
159 "HRULE:%s_critical_c_min#000000:%-15s" % (id, _("Critical")),
160 "GPRINT:%s_critical_c_min:%%6.2lf °C\\r",
161 ]
162 break
163
164 return rrd_graph
165
166 @property
167 def graph_title(self):
168 _ = self.locale.translate
169 return _("Processor")
170
171 @property
172 def graph_vertical_label(self):
173 _ = self.locale.translate
174 return _("Temperature")
175
176
177 class SensorBaseObject(base.Object):
178 def init(self, sensor):
179 self.sensor = sensor
180
181 def __repr__(self):
182 return "<%s %s (%s)>" % (self.__class__.__name__, self.sensor.name, self.sensor.label)
183
184 @property
185 def id(self):
186 return "-".join((self.sensor.name, self.sensor.label))
187
188 @property
189 def type(self):
190 return self.sensor.type
191
192
193 class SensorTemperatureObject(SensorBaseObject):
194 rrd_schema = [
195 "DS:value:GAUGE:0:U",
196 "DS:critical:GAUGE:0:U",
197 "DS:low:GAUGE:0:U",
198 "DS:high:GAUGE:0:U",
199 ]
200
201 def collect(self):
202 assert self.type == "temperature"
203
204 return (
205 self.sensor.value,
206 self.critical,
207 self.low,
208 self.high,
209 )
210
211 @property
212 def critical(self):
213 try:
214 return self.sensor.critical
215 except AttributeError:
216 return "NaN"
217
218 @property
219 def low(self):
220 try:
221 return self.sensor.minimum
222 except AttributeError:
223 return "NaN"
224
225 @property
226 def high(self):
227 try:
228 return self.sensor.high
229 except AttributeError:
230 return "NaN"
231
232
233 class SensorVoltageObject(SensorBaseObject):
234 rrd_schema = [
235 "DS:value:GAUGE:0:U",
236 "DS:minimum:GAUGE:0:U",
237 "DS:maximum:GAUGE:0:U",
238 ]
239
240 def collect(self):
241 assert self.type == "voltage"
242
243 return (
244 self.sensor.value,
245 self.minimum,
246 self.maximum,
247 )
248
249 @property
250 def minimum(self):
251 try:
252 return self.sensor.minimum
253 except AttributeError:
254 return "NaN"
255
256 @property
257 def maximum(self):
258 try:
259 return self.sensor.maximum
260 except AttributeError:
261 return "NaN"
262
263
264 class SensorFanObject(SensorBaseObject):
265 rrd_schema = [
266 "DS:value:GAUGE:0:U",
267 "DS:minimum:GAUGE:0:U",
268 "DS:maximum:GAUGE:0:U",
269 ]
270
271 def collect(self):
272 assert self.type == "fan"
273
274 return (
275 self.sensor.value,
276 self.minimum,
277 self.maximum,
278 )
279
280 @property
281 def minimum(self):
282 try:
283 return self.sensor.minimum
284 except AttributeError:
285 return "NaN"
286
287 @property
288 def maximum(self):
289 try:
290 return self.sensor.maximum
291 except AttributeError:
292 return "NaN"
293
294
295 class SensorsPlugin(base.Plugin):
296 name = "sensors"
297 description = "Sensors Plugin"
298
299 templates = [
300 GraphTemplateSensorsProcessorTemperature,
301 GraphTemplateSensorsTemperature,
302 ]
303
304 def init(self):
305 # Initialise the sensors library.
306 _collecty.sensors_init()
307
308 def __del__(self):
309 _collecty.sensors_cleanup()
310
311 @property
312 def objects(self):
313 return self.get_detected_sensor_objects()
314
315 def get_detected_sensor_objects(self, what=None):
316 for sensor in _collecty.get_detected_sensors(what):
317 if sensor.type == "temperature":
318 yield SensorTemperatureObject(self, sensor)
319
320 elif sensor.type == "voltage":
321 yield SensorVoltageObject(self, sensor)
322
323 elif sensor.type == "fan":
324 yield SensorFanObject(self, sensor)