Cleanup config.nodes_of
[check_mk.git] / checks / openhardwaremonitor
blobef6b26764ed0ed902e04d89b700902dd70539deb
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # <<<openhardwaremonitor:sep(44)>>>
28 # Index,Name,Parent,SensorType,Value
29 # 1,Available Memory,/ram,Data,3.009140
30 # 0,CPU Total,/intelcpu/0,Load,1.953125
31 # 0,Used Space,/hdd/0,Load,58.754211
32 # 4,CPU Core #4,/intelcpu/0,Load,0.000000
33 # 2,CPU Graphics,/intelcpu/0,Power,0.000000
34 # 2,CPU Core #2,/intelcpu/0,Load,6.250000
35 # 4,CPU Core #4,/intelcpu/0,Clock,3192.720947
36 # 0,Bus Speed,/intelcpu/0,Clock,99.772530
37 # 3,CPU DRAM,/intelcpu/0,Power,0.000000
38 # 3,CPU Core #3,/intelcpu/0,Load,0.000000
39 # 1,CPU Core #1,/intelcpu/0,Clock,3192.720947
40 # 3,CPU Core #3,/intelcpu/0,Clock,3192.720947
41 # 0,Memory,/ram,Load,24.763321
42 # 0,Used Memory,/ram,Data,0.990425
43 # 2,CPU Core #2,/intelcpu/0,Clock,3192.720947
44 # 0,CPU Package,/intelcpu/0,Power,0.000000
45 # 1,CPU Cores,/intelcpu/0,Power,0.000000
46 # 1,CPU Core #1,/intelcpu/0,Load,1.562500
48 # Newer agent data provide WMIStatus column
49 # Index,Name,Parent,SensorType,Value,WMIStatus
50 # 1,CPU Core #1,/intelcpu/0,Load,1.562500,OK/Timeout
52 # since the temperature sensors could be anything (cpu, gpu, hdd, psu) we need different
53 # default levels per item type
54 factory_settings['openhardwaremonitor_temperature_default_levels'] = {
55 "cpu": {
56 'levels': (60, 70)
58 "hdd": {
59 'levels': (40, 50)
61 "_default": {
62 'levels': (70, 80)
66 OpenhardwaremonitorTraits = {
67 'Clock': {
68 'unit': " MHz",
69 'factor': 1.0,
70 'perf_var': 'clock'
72 'Temperature': {
73 'unit': u"°C",
74 'factor': 1.0
76 'Power': {
77 'unit': " W",
78 'factor': 1.0,
79 'perf_var': 'w'
81 'Fan': {
82 'unit': " RPM",
83 'factor': 1.0
85 'Level': {
86 'unit': "%",
87 'factor': 1.0
89 # unused below here
90 'Voltage': {
91 'unit': " V",
92 'factor': 1.0
94 'Load': {
95 'unit': "%",
96 'factor': 1.0
98 'Flow': {
99 'unit': " L/h",
100 'factor': 1.0
102 'Control': {
103 'unit': "%",
104 'factor': 1.0
106 'Factor': {
107 'unit': "1",
108 'factor': 1.0
110 'Data': {
111 'unit': " B",
112 'factor': 1073741824.0
115 OpenhardwaremonitorSensor = collections.namedtuple("OpenhardwaremonitorSensor",
116 ("reading", "unit", "perf_var", "WMIstatus"))
119 def parse_openhardwaremonitor(info):
120 parsed = {}
121 for line in info:
122 if line[0] == 'Index':
123 # header line
124 continue
126 if len(line) == 5:
127 # Old agent output has no WMIStatus column
128 _index, name, parent, sensor_type, value = line
129 wmistatus = 'OK'
130 elif len(line) == 6:
131 _index, name, parent, sensor_type, value, wmistatus = line
132 else:
133 continue
135 full_name = _create_openhardwaremonitor_full_name(parent, name)
136 traits = OpenhardwaremonitorTraits.get(sensor_type, {})
137 parsed.setdefault(sensor_type, {}).setdefault(
138 full_name,
139 OpenhardwaremonitorSensor(
140 float(value) * traits.get('factor', 1), traits.get('unit', ''),
141 traits.get('perf_var'), wmistatus))
142 return parsed
145 def _create_openhardwaremonitor_full_name(parent, name):
146 def dict_replace(input_, replacements):
147 pattern = regex(r'\b(' + '|'.join(replacements.keys()) + r')\b')
148 return pattern.sub(lambda x: replacements[x.group()], input_)
150 parent = dict_replace(parent, {"intelcpu": "cpu", "amdcpu": "cpu", "genericcpu": "cpu"})
151 name = dict_replace(name, {"CPU ": "", "Temperature": ""})
152 return (parent.replace("/", "") + " " + name).strip()
155 def _openhardwaremonitor_worst_status(*args):
156 order = [0, 1, 3, 2]
157 return sorted(args, key=lambda x: order[x], reverse=True)[0]
160 def _openhardwaremonitor_expect_order(*args):
161 arglist = [x for x in args if x is not None]
162 sorted_by_val = sorted(enumerate(arglist), key=lambda x: x[1])
163 return max([abs(x[0] - x[1][0]) for x in enumerate(sorted_by_val)])
166 def inventory_openhardwaremonitor(sensor_type, parsed):
167 return [(key, {}) for key in parsed.get(sensor_type, {}).keys()]
170 def check_openhardwaremonitor(sensor_type, item, params, parsed):
171 if item in parsed.get(sensor_type, {}):
172 data = parsed[sensor_type][item]
173 _check_openhardwaremonitor_wmistatus(data)
174 if 'lower' in params:
175 status_lower = _openhardwaremonitor_expect_order(params['lower'][1], params['lower'][0],
176 data.reading)
177 else:
178 status_lower = 0
179 if 'upper' in params:
180 status_upper = _openhardwaremonitor_expect_order(data.reading, params['upper'][0],
181 params['upper'][1])
182 else:
183 status_upper = 0
185 perfdata = []
186 if data.perf_var:
187 perfdata = [(data.perf_var, data.reading)]
189 return (_openhardwaremonitor_worst_status(status_lower, status_upper),
190 '%.1f%s' % (data.reading, data.unit), perfdata)
193 def _check_openhardwaremonitor_wmistatus(data):
194 if data.WMIstatus.lower() == 'timeout':
195 raise MKCounterWrapped('WMI query timed out')
198 # .--clock---------------------------------------------------------------.
199 # | _ _ |
200 # | ___| | ___ ___| | __ |
201 # | / __| |/ _ \ / __| |/ / |
202 # | | (__| | (_) | (__| < |
203 # | \___|_|\___/ \___|_|\_\ |
204 # | |
205 # '----------------------------------------------------------------------'
207 check_info['openhardwaremonitor'] = {
208 'inventory_function': lambda parsed: inventory_openhardwaremonitor('Clock', parsed),
209 'check_function': lambda item, params, parsed: check_openhardwaremonitor(
210 'Clock', item, params, parsed),
211 'parse_function': parse_openhardwaremonitor,
212 'has_perfdata': True,
213 'service_description': "Clock %s",
217 # .--temp----------------------------------------------------------------.
218 # | _ |
219 # | | |_ ___ _ __ ___ _ __ |
220 # | | __/ _ \ '_ ` _ \| '_ \ |
221 # | | || __/ | | | | | |_) | |
222 # | \__\___|_| |_| |_| .__/ |
223 # | |_| |
224 # '----------------------------------------------------------------------'
227 def check_openhardwaremonitor_temperature(item, params, parsed):
228 if not 'levels' in params:
229 found = False
230 for key in params.iterkeys():
231 if key in item:
232 params = params[key]
233 found = True
234 break
235 if not found:
236 params = params["_default"]
238 if item in parsed.get('Temperature', {}):
239 data = parsed['Temperature'][item]
240 _check_openhardwaremonitor_wmistatus(data)
241 return check_temperature(data.reading, params, "openhardwaremonitor_%s" % item)
244 check_info['openhardwaremonitor.temperature'] = {
245 'inventory_function': lambda parsed: inventory_openhardwaremonitor('Temperature', parsed),
246 'check_function': check_openhardwaremonitor_temperature,
247 'has_perfdata': True,
248 'service_description': "Temperature %s",
249 'group': 'temperature',
250 'default_levels_variable': 'openhardwaremonitor_temperature_default_levels',
251 'includes': ['temperature.include']
255 # .--power---------------------------------------------------------------.
256 # | |
257 # | _ __ _____ _____ _ __ |
258 # | | '_ \ / _ \ \ /\ / / _ \ '__| |
259 # | | |_) | (_) \ V V / __/ | |
260 # | | .__/ \___/ \_/\_/ \___|_| |
261 # | |_| |
262 # '----------------------------------------------------------------------'
264 check_info['openhardwaremonitor.power'] = {
265 'inventory_function': lambda parsed: inventory_openhardwaremonitor('Power', parsed),
266 'check_function': lambda item, params, parsed: check_openhardwaremonitor(
267 'Power', item, params, parsed),
268 'has_perfdata': True,
269 'service_description': "Power %s",
273 # .--fan-----------------------------------------------------------------.
274 # | __ |
275 # | / _| __ _ _ __ |
276 # | | |_ / _` | '_ \ |
277 # | | _| (_| | | | | |
278 # | |_| \__,_|_| |_| |
279 # | |
280 # '----------------------------------------------------------------------'
282 factory_settings['openhardwaremonitor_fan_default_levels'] = {
283 'lower': (None, None),
284 'upper': (None, None),
288 def check_openhardwaremonitor_fan(item, params, parsed):
289 if item in parsed.get("Fan", {}):
290 data = parsed["Fan"][item]
291 _check_openhardwaremonitor_wmistatus(data)
292 return check_fan(data.reading, params)
295 check_info['openhardwaremonitor.fan'] = {
296 'inventory_function': lambda parsed: inventory_openhardwaremonitor('Fan', parsed),
297 'check_function': check_openhardwaremonitor_fan,
298 'has_perfdata': True,
299 'service_description': "Fan %s",
300 'default_levels_variable': 'openhardwaremonitor_fan_default_levels',
301 'group': 'hw_fans',
302 'includes': ['fan.include'],
306 # .--smart---------------------------------------------------------------.
307 # | _ |
308 # | ___ _ __ ___ __ _ _ __| |_ |
309 # | / __| '_ ` _ \ / _` | '__| __| |
310 # | \__ \ | | | | | (_| | | | |_ |
311 # | |___/_| |_| |_|\__,_|_| \__| |
312 # | |
313 # '----------------------------------------------------------------------'
315 openhardwaremonitor_smart_readings = {
316 'Level': [{
317 'name': "Remaining Life",
318 'key': 'remaining_life',
319 'lower_bounds': True
323 factory_settings['openhardwaremonitor_smart_default_levels'] = {
324 'remaining_life': (30, 10), # wild guess
328 def inventory_openhardwaremonitor_smart(parsed):
329 devices = set()
330 # find all devices for which at least one known smart reading is available
331 for sensor_type in openhardwaremonitor_smart_readings:
332 for key in parsed.get(sensor_type, {}):
333 if "hdd" in key:
334 devices.add(key.split(" ")[0])
335 return [(dev, {}) for dev in devices]
338 def check_openhardwaremonitor_smart(item, params, parsed):
339 for sensor_type, readings in openhardwaremonitor_smart_readings.iteritems():
340 for reading in readings:
341 reading_name = "%s %s" % (item, reading['name'])
343 if not reading_name in parsed[sensor_type]:
344 # what smart values ohm reports is device dependent
345 continue
347 warn, crit = params[reading['key']]
348 data = parsed[sensor_type][reading_name]
349 _check_openhardwaremonitor_wmistatus(data)
351 if reading.get('lower_bounds', False):
352 status = _openhardwaremonitor_expect_order(crit, warn, data.reading)
353 else:
354 status = _openhardwaremonitor_expect_order(data.reading, warn, crit)
356 yield (
357 status,
358 "%s %.1f%s" % (reading['name'], data.reading, data.unit),
359 [(reading['key'], data.reading)],
363 # the smart check is different from the others as it has one item per device and
364 # combines different sensors per item (but not all, i.e. hdd temperature is still
365 # reported as a temperature item)
366 check_info['openhardwaremonitor.smart'] = {
367 'inventory_function': inventory_openhardwaremonitor_smart,
368 'check_function': check_openhardwaremonitor_smart,
369 'has_perfdata': True,
370 'service_description': "SMART %s Stats",
371 'default_levels_variable': "openhardwaremonitor_smart_default_levels",
372 'group': "openhardwaremonitor_smart"