@cryptotaxi247 / netdata-1 / commits / 8a9b25e9a

sensors

paulfantom committed Jul 7, 2016 at 18:31 UTC 8a9b25e9a1c94f76ba748b116c654c5a1f25c3ad
3 files changed +417 -1
python.d/Makefile.am
+2 -1
@@ -18,6 +18,7 @@ dist_python_SCRIPTS = \
18 nginx.chart.py \
19 phpfpm.chart.py \
20 postfix.chart.py \
21 + sensors.chart.py \
22 squid.chart.py \
23 tomcat.chart.py \
24 python-modules-installer.sh \
@@ -32,5 +33,5 @@ dist_pythonmodules_DATA = \
33 python_modules/__init__.py \
34 python_modules/base.py \
35 python_modules/msg.py \
36 + python_modules/lm_sensors.py \
37 $(NULL)
36 -
python.d/python_modules/lm_sensors.py new
+309
@@ -0,0 +1,309 @@
1 +#!/usr/bin/env python
2 +# -*- coding: utf-8 -*-
3 +import os
4 +
5 +from ctypes import CDLL, c_char_p, c_int, c_void_p, c_uint, c_double, byref, Structure, get_errno,\
6 + POINTER, c_short, c_size_t, create_string_buffer
7 +from ctypes.util import find_library
8 +
9 +version_info = (0, 0, 3)
10 +
11 +__version__ = '.'.join(map(str, version_info))
12 +__date__ = '2014-08-17'
13 +__author__ = "Marc 'BlackJack' Rintsch"
14 +__contact__ = 'marc@rintsch.de'
15 +__license__ = 'LGPL v2.1'
16 +
17 +API_VERSION = 4
18 +DEFAULT_CONFIG_FILENAME = '/etc/sensors3.conf'
19 +
20 +LIB_FILENAME = os.environ.get('SENSORS_LIB') or find_library('sensors')
21 +SENSORS_LIB = CDLL(LIB_FILENAME)
22 +VERSION = c_char_p.in_dll(SENSORS_LIB, 'libsensors_version').value
23 +MAJOR_VERSION = version_info[0]
24 +STDC_LIB = CDLL(find_library('c'), use_errno=True)
25 +
26 +TYPE_DICT = {
27 + 0: 'voltage',
28 + 1: 'fan',
29 + 2: 'temperature',
30 + 3: 'power',
31 + 4: 'energy',
32 + 5: 'current',
33 + 6: 'humidity',
34 + 7: 'max_main',
35 + 10: 'vid',
36 + 11: 'intrusion',
37 + 12: 'max_other',
38 + 18: 'beep_enable'
39 +}
40 +
41 +class SensorsError(Exception):
42 + def __init__(self, message, error_number=None):
43 + Exception.__init__(self, message)
44 + self.error_number = error_number
45 +
46 +
47 +def _error_check(result, _func, _arguments):
48 + if result < 0:
49 + raise SensorsError(_strerror(result), result)
50 + return result
51 +
52 +_strerror = SENSORS_LIB.sensors_strerror
53 +_strerror.argtypes = [c_int]
54 +_strerror.restype = c_char_p
55 +
56 +_init = SENSORS_LIB.sensors_init
57 +_init.argtypes = [c_void_p]
58 +_init.restype = c_int
59 +_init.errcheck = _error_check
60 +
61 +cleanup = SENSORS_LIB.sensors_cleanup
62 +cleanup.argtypes = None
63 +cleanup.restype = None
64 +
65 +
66 +def init(config_filename=DEFAULT_CONFIG_FILENAME):
67 + file_p = STDC_LIB.fopen(config_filename.encode('utf-8'), b'r')
68 + if file_p is None:
69 + error_number = get_errno()
70 + raise OSError(error_number, os.strerror(error_number), config_filename)
71 + try:
72 + _init(file_p)
73 + finally:
74 + STDC_LIB.fclose(file_p)
75 +
76 +
77 +class Subfeature(Structure):
78 + _fields_ = [
79 + ('name', c_char_p),
80 + ('number', c_int),
81 + ('type', c_int),
82 + ('mapping', c_int),
83 + ('flags', c_uint),
84 + ]
85 +
86 + def __repr__(self):
87 + return '<%s name=%r number=%d type=%d mapping=%d flags=%08x>' % (
88 + self.__class__.__name__,
89 + self.name,
90 + self.number,
91 + self.type,
92 + self.mapping,
93 + self.flags
94 + )
95 +
96 + def get_value(self):
97 + result = c_double()
98 + _get_value(byref(self.parent.chip), self.number, byref(result))
99 + return result.value
100 +
101 +SUBFEATURE_P = POINTER(Subfeature)
102 +
103 +
104 +class Feature(Structure):
105 + _fields_ = [
106 + ('name', c_char_p),
107 + ('number', c_int),
108 + ('type', c_int),
109 + ('_first_subfeature', c_int),
110 + ('_padding1', c_int),
111 + ]
112 +
113 + def __repr__(self):
114 + return '<%s name=%r number=%r type=%r>' % (
115 + self.__class__.__name__,
116 + self.name,
117 + self.number,
118 + self.type
119 + )
120 +
121 + def __iter__(self):
122 + number = c_int(0)
123 + while True:
124 + result_p = _get_all_subfeatures(
125 + byref(self.chip),
126 + byref(self),
127 + byref(number)
128 + )
129 + if not result_p:
130 + break
131 + result = result_p.contents
132 + result.chip = self.chip
133 + result.parent = self
134 + yield result
135 +
136 + @property
137 + def label(self):
138 + #
139 + # TODO Maybe this is a memory leak!
140 + #
141 + return _get_label(byref(self.chip), byref(self)).decode('utf-8')
142 +
143 + def get_value(self):
144 + #
145 + # TODO Is the first always the correct one for all feature types?
146 + #
147 + return next(iter(self)).get_value()
148 +
149 +FEATURE_P = POINTER(Feature)
150 +
151 +
152 +class Bus(Structure):
153 + TYPE_ANY = -1
154 + NR_ANY = -1
155 +
156 + _fields_ = [
157 + ('type', c_short),
158 + ('nr', c_short),
159 + ]
160 +
161 + def __str__(self):
162 + return (
163 + '*' if self.type == self.TYPE_ANY
164 + else _get_adapter_name(byref(self)).decode('utf-8')
165 + )
166 +
167 + def __repr__(self):
168 + return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.nr)
169 +
170 + @property
171 + def has_wildcards(self):
172 + return self.type == self.TYPE_ANY or self.nr == self.NR_ANY
173 +
174 +BUS_P = POINTER(Bus)
175 +
176 +
177 +class Chip(Structure):
178 + #
179 + # TODO Move common stuff into `AbstractChip` class.
180 + #
181 + _fields_ = [
182 + ('prefix', c_char_p),
183 + ('bus', Bus),
184 + ('addr', c_int),
185 + ('path', c_char_p),
186 + ]
187 +
188 + PREFIX_ANY = None
189 + ADDR_ANY = -1
190 +
191 + def __new__(cls, *args):
192 + result = super(Chip, cls).__new__(cls)
193 + if args:
194 + _parse_chip_name(args[0].encode('utf-8'), byref(result))
195 + return result
196 +
197 + def __init__(self, *_args):
198 + Structure.__init__(self)
199 + #
200 + # Need to bind the following to the instance so it is available in
201 + # `__del__()` when the interpreter shuts down.
202 + #
203 + self._free_chip_name = _free_chip_name
204 + self.byref = byref
205 +
206 + def __del__(self):
207 + if self._b_needsfree_:
208 + self._free_chip_name(self.byref(self))
209 +
210 + def __repr__(self):
211 + return '<%s prefix=%r bus=%r addr=%r path=%r>' % (
212 + (
213 + self.__class__.__name__,
214 + self.prefix,
215 + self.bus,
216 + self.addr,
217 + self.path
218 + )
219 + )
220 +
221 + def __str__(self):
222 + buffer_size = 200
223 + result = create_string_buffer(buffer_size)
224 + used = _snprintf_chip_name(result, len(result), byref(self))
225 + assert used < buffer_size
226 + return result.value.decode('utf-8')
227 +
228 + def __iter__(self):
229 + number = c_int(0)
230 + while True:
231 + result_p = _get_features(byref(self), byref(number))
232 + if not result_p:
233 + break
234 + result = result_p.contents
235 + result.chip = self
236 + yield result
237 +
238 + @property
239 + def adapter_name(self):
240 + return str(self.bus)
241 +
242 + @property
243 + def has_wildcards(self):
244 + return (
245 + self.prefix == self.PREFIX_ANY
246 + or self.addr == self.ADDR_ANY
247 + or self.bus.has_wildcards
248 + )
249 +
250 +CHIP_P = POINTER(Chip)
251 +
252 +
253 +_parse_chip_name = SENSORS_LIB.sensors_parse_chip_name
254 +_parse_chip_name.argtypes = [c_char_p, CHIP_P]
255 +_parse_chip_name.restype = c_int
256 +_parse_chip_name.errcheck = _error_check
257 +
258 +_free_chip_name = SENSORS_LIB.sensors_free_chip_name
259 +_free_chip_name.argtypes = [CHIP_P]
260 +_free_chip_name.restype = None
261 +
262 +_snprintf_chip_name = SENSORS_LIB.sensors_snprintf_chip_name
263 +_snprintf_chip_name.argtypes = [c_char_p, c_size_t, CHIP_P]
264 +_snprintf_chip_name.restype = c_int
265 +_snprintf_chip_name.errcheck = _error_check
266 +
267 +_get_adapter_name = SENSORS_LIB.sensors_get_adapter_name
268 +_get_adapter_name.argtypes = [BUS_P]
269 +_get_adapter_name.restype = c_char_p
270 +
271 +_get_label = SENSORS_LIB.sensors_get_label
272 +_get_label.argtypes = [CHIP_P, FEATURE_P]
273 +_get_label.restype = c_char_p
274 +
275 +_get_value = SENSORS_LIB.sensors_get_value
276 +_get_value.argtypes = [CHIP_P, c_int, POINTER(c_double)]
277 +_get_value.restype = c_int
278 +_get_value.errcheck = _error_check
279 +
280 +#
281 +# TODO sensors_set_value()
282 +# TODO sensors_do_chip_sets()
283 +#
284 +
285 +_get_detected_chips = SENSORS_LIB.sensors_get_detected_chips
286 +_get_detected_chips.argtypes = [CHIP_P, POINTER(c_int)]
287 +_get_detected_chips.restype = CHIP_P
288 +
289 +_get_features = SENSORS_LIB.sensors_get_features
290 +_get_features.argtypes = [CHIP_P, POINTER(c_int)]
291 +_get_features.restype = FEATURE_P
292 +
293 +_get_all_subfeatures = SENSORS_LIB.sensors_get_all_subfeatures
294 +_get_all_subfeatures.argtypes = [CHIP_P, FEATURE_P, POINTER(c_int)]
295 +_get_all_subfeatures.restype = SUBFEATURE_P
296 +
297 +#
298 +# TODO sensors_get_subfeature() ?
299 +#
300 +
301 +
302 +def iter_detected_chips(chip_name='*-*'):
303 + chip = Chip(chip_name)
304 + number = c_int(0)
305 + while True:
306 + result = _get_detected_chips(byref(chip), byref(number))
307 + if not result:
308 + break
309 + yield result.contents
\ No newline at end of file
python.d/sensors.chart.py new
+106
@@ -0,0 +1,106 @@
1 +# -*- coding: utf-8 -*-
2 +# Description: sensors netdata python.d plugin
3 +# Author: Pawel Krupa (paulfantom)
4 +
5 +from base import SimpleService
6 +import lm_sensors as sensors
7 +
8 +# default module values (can be overridden per job in `config`)
9 +# update_every = 2
10 +
11 +ORDER = ['temperature', 'voltage', 'fan', 'power', 'current', 'energy', 'humidity']
12 +
13 +# This is a prototype of chart definition which is used to dynamically create self.definitions
14 +CHARTS = {
15 + 'temperature': {
16 + 'options': [None, ' temperature', 'Celsius', 'temperature', 'sensors.temp', 'line'],
17 + 'lines': [
18 + [None, None, 'absolute', 1, 1000]
19 + ]},
20 + 'voltage': {
21 + 'options': [None, ' voltage', 'Volts', 'voltage', 'sensors.volt', 'line'],
22 + 'lines': [
23 + [None, None, 'absolute', 1, 1000]
24 + ]},
25 + 'current': {
26 + 'options': [None, ' current', 'Ampere', 'current', 'sensors.curr', 'line'],
27 + 'lines': [
28 + [None, None, 'absolute', 1, 1000]
29 + ]},
30 + 'power': {
31 + 'options': [None, ' power', 'Watt', 'power', 'sensors.power', 'line'],
32 + 'lines': [
33 + [None, None, 'absolute', 1, 1000000]
34 + ]},
35 + 'fan': {
36 + 'options': [None, ' fans speed', 'Rotations/min', 'fans', 'sensors.fans', 'line'],
37 + 'lines': [
38 + [None, None, 'absolute', 1, 1000]
39 + ]},
40 + 'energy': {
41 + 'options': [None, ' energy', 'Joule', 'energy', 'sensors.energy', 'areastack'],
42 + 'lines': [
43 + [None, None, 'incremental', 1, 1000000]
44 + ]},
45 + 'humidity': {
46 + 'options': [None, ' humidity', 'Percent', 'humidity', 'sensors.humidity', 'line'],
47 + 'lines': [
48 + [None, None, 'absolute', 1, 1000]
49 + ]}
50 +}
51 +
52 +
53 +class Service(SimpleService):
54 + def __init__(self, configuration=None, name=None):
55 + SimpleService.__init__(self, configuration=configuration, name=name)
56 + self.order = []
57 + self.definitions = {}
58 +
59 + def _get_data(self):
60 + data = {}
61 + try:
62 + for chip in sensors.iter_detected_chips():
63 + prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
64 + lines = {}
65 + for feature in chip:
66 + data[prefix + "_" + str(feature.name.decode())] = feature.get_value()
67 + except Exception as e:
68 + self.error(e)
69 + return None
70 +
71 + if len(data) == 0:
72 + return None
73 + return data
74 +
75 + def _create_definitions(self):
76 + for type in ORDER:
77 + for chip in sensors.iter_detected_chips():
78 + prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
79 + name = ""
80 + lines = []
81 + for feature in chip:
82 + if sensors.TYPE_DICT[feature.type] == type:
83 + name = str(chip.prefix.decode()) + "_" + sensors.TYPE_DICT[feature.type]
84 + if name not in self.order:
85 + options = list(CHARTS[type]['options'])
86 + options[1] = str(chip.prefix) + options[1]
87 + self.definitions[name] = {'options': options}
88 + self.definitions[name]['lines'] = []
89 + self.order.append(name)
90 + line = list(CHARTS[type]['lines'][0])
91 + line[0] = prefix + "_" + str(feature.name.decode())
92 + line[1] = str(feature.label)
93 + print(line)
94 + self.definitions[name]['lines'].append(line)
95 +
96 + def check(self):
97 + try:
98 + sensors.init()
99 + except Exception as e:
100 + print(e)
101 + return False
102 + try:
103 + self._create_definitions()
104 + except:
105 + return False
106 + return True
\ No newline at end of file