]> git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/sensors.py
sensors: Make the lines less fat
[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 # 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,-",
44
45 # Change colour when the value gets above high
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",
48
49 # Get data points for the threshold lines
50 "VDEF:critical_c_line=critical_c,MINIMUM",
51 "VDEF:low_c_line=low_c,MAXIMUM",
52
53 # Draw the temperature value
54 "LINE3:value_c_high#ff0000",
55 "LINE2:value_c_normal#00ff00:%-15s" % _("Temperature"),
56
57 # Draw the legend
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"),
62
63 # Empty line
64 "COMMENT: \\n",
65
66 # Draw boundary lines
67 "COMMENT:%s\:" % _("Temperature Thresholds"),
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",
72 ]
73
74 @property
75 def graph_title(self):
76 _ = self.locale.translate
77 return _("Temperature (%s)") % self.object.sensor.name
78
79 @property
80 def graph_vertical_label(self):
81 _ = self.locale.translate
82 return _("° Celsius")
83
84
85 class GraphTemplateSensorsProcessorTemperature(base.GraphTemplate):
86 name = "processor-temperature"
87
88 core_colours = [
89 "#ff000033",
90 "#0000ff33",
91 "#00ff0033",
92 "#0000ff33",
93 ]
94
95 def get_temperature_sensors(self):
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
104
105 def get_objects(self, *args, **kwargs):
106 sensors = self.get_temperature_sensors()
107
108 return list(sensors)
109
110 @property
111 def rrd_graph(self):
112 _ = self.locale.translate
113 rrd_graph = []
114
115 counter = 0
116 ids = []
117
118 for core in self.objects:
119 id = "core%s" % counter
120 counter += 1
121 ids.append(id)
122
123 rrd_graph += core.make_rrd_defs(id) + [
124 # Convert everything to celsius
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),
128 ]
129
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 ]
136
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
142
143 rrd_graph += [
144 # Change colour when the value gets above high
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",
147
148 "LINE2:all_value_c_high#FF0000",
149 "LINE2:all_value_c_normal#000000:%-15s\l" % _("Temperature"),
150
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"),
154 ]
155
156 for id, core, colour in zip(ids, self.objects, self.core_colours):
157 rrd_graph += [
158 # TODO these lines were supposed to be dashed, but that
159 # didn't really work here
160 "LINE1:%s_value_c%s:%-10s" % (id, colour, core.sensor.label),
161 ]
162
163 # Draw the critical line
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
170
171 return rrd_graph
172
173 @property
174 def graph_title(self):
175 _ = self.locale.translate
176 return _("Processor")
177
178 @property
179 def graph_vertical_label(self):
180 _ = self.locale.translate
181 return _("Temperature")
182
183
184 class 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
200 class 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
240 class 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
271 class 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
288 def minimum(self):
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
302 class 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)