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