Cleanup config.nodes_of
[check_mk.git] / checks / emka_modules
blobc7b9cc3d815154d577177dd09fe8994c2c0580bf
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2017 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.
28 def parse_emka_modules(info):
29 map_module_types = { # basModuleCoIx == 0
30 "0" : "vacant",
31 "8" : "U8, keypad",
32 "9" : "U9, card module (proximity)",
33 "10" : "U10, phone module (modem)",
34 "11" : "U11/U32, up to 8 handles / single point latches",
35 "12" : "U12/U33, up to 2 handles / single point latches",
36 "13" : "U13, 4 sensors and 4 relays",
37 "14" : "U14, communication module",
38 "15" : "fultifunction module M15",
39 "16" : "fultifunction module M16", }
41 map_component_types = { # independent of module type, basModuleCoIx > 0
42 "1" : "alarm",
43 "2" : "handle",
44 "3" : "sensor",
45 "4" : "relay",
46 "5" : "keypad",
47 "6" : "card_terminal",
48 "7" : "phone_modem",
49 "8" : "analogous_output", }
51 parsed = {"basic_components": {}}
52 for oidend, status, ty, mod_info, remark in info[0]:
53 mo_index, co_index = oidend.split(".")
54 if mo_index == "0":
55 itemname = "Master %s" % mod_info.split(",")[0]
56 else:
57 itemname = "Perip %s %s" % (mo_index, mod_info)
59 if co_index == "0":
60 parsed["basic_components"].setdefault(
61 itemname.strip(), {
62 "type": map_module_types[co_index],
63 "activation": status,
64 "_location_": "0.%s" % mo_index,
66 continue
68 table = map_component_types[ty]
69 if remark == "":
70 itemname = oidend
71 else:
72 itemname = "%s %s" % (remark, oidend)
74 parsed.setdefault(table, {})
75 parsed[table].setdefault(itemname, {"_location_": oidend})
77 for oidend, module_link, value, mode in info[1]:
78 table = map_component_types[oidend.split(".", 1)[0]]
79 location = ".".join(module_link.split(".")[-2:])
80 for entry, attrs in parsed.get(table, {}).items():
81 item_location = attrs["_location_"]
82 if item_location != location:
83 continue
85 attrs["value"] = value
86 if mode:
87 attrs["mode"] = mode
89 for oidend, threshold in info[2]:
90 location, threshold_ty = oidend.split(".")
91 if threshold_ty == "1":
92 ty = "levels_lower"
93 else:
94 ty = "levels"
96 for entry, attrs in parsed.get("sensor", {}).items():
97 if attrs["_location_"].startswith("%s." % location):
98 attrs[ty] = (threshold, threshold)
100 # Explanation from ELM2-MIB:
101 # Empty string -> default, that means value in mV.
102 # Universal: {[factor]}[unit]=$mV*[multiplicator]/[divisor]+[offset]
103 # [multiplicator], [divisor], [offset] must be integers
104 # Example: {0.1}%=$mV*20/100-100
105 # default: {1}mV=$mV*1/1+0"
106 # From the walk we get an ascii coded list separated by null bytes.
107 # Results in: "=#\xb0C0.02-30.0" where
108 # \xb0C => Temperature
109 # 0.02 => 2/100 [multiplicator]/[divisor]
110 # -30.0 [offset]
111 # Notice, may also "=#\xb0C0.0230.0"
112 for oidend, equation_bin in info[3]:
113 equation = []
114 part = []
115 for entry in equation_bin:
116 if entry:
117 part.append(entry)
118 elif part:
119 equation.append("".join(map(chr, part)))
120 part = []
122 if not equation:
123 continue
125 if equation[0].endswith("#\xb0C"):
126 sensor_ty = "sensor_temp"
127 elif equation[0].endswith("#%RF"):
128 sensor_ty = "sensor_humid"
129 else:
130 sensor_ty = "sensor_volt"
132 equation = equation[1:]
133 if len(equation) == 2:
134 m, a = map(float, equation)
135 else:
136 m, a = 1.0, 0.0
138 def scale_f(x, m=m, a=a):
139 return float(x) * m + a
141 location = str(chr(oidend[0]))
142 for sensor, attrs in parsed.get("sensor", {}).items():
143 if attrs["_location_"].endswith(".%s" % location):
144 parsed.setdefault(sensor_ty, {})
145 parsed[sensor_ty].setdefault(
146 sensor, {
147 "value": scale_f(attrs["value"]),
148 "levels": map(scale_f, attrs["levels"]),
149 "levels_lower": map(scale_f, attrs["levels_lower"]),
151 break
153 # Cleanup
154 if "sensor" in parsed:
155 del parsed["sensor"]
157 return parsed
160 # .--component-----------------------------------------------------------.
161 # | _ |
162 # | ___ ___ _ __ ___ _ __ ___ _ __ ___ _ __ | |_ |
163 # | / __/ _ \| '_ ` _ \| '_ \ / _ \| '_ \ / _ \ '_ \| __| |
164 # | | (_| (_) | | | | | | |_) | (_) | | | | __/ | | | |_ |
165 # | \___\___/|_| |_| |_| .__/ \___/|_| |_|\___|_| |_|\__| |
166 # | |_| |
167 # +----------------------------------------------------------------------+
168 # | main check |
169 # '----------------------------------------------------------------------'
172 def inventory_emka_modules(parsed):
173 for entry, attrs in parsed["basic_components"].items():
174 if attrs["activation"] != "i":
175 yield entry, None
178 def check_emka_modules(item, params, parsed):
179 map_activation_states = {
180 '-': (0, "vacant"),
181 '?': (0, "detect modus"),
182 'x': (0, "excluded"),
183 'e': (2, "error"),
184 'c': (2, "collision detected"),
185 'w': (1, "wait for dynamic address"),
186 'P': (1, "polling"),
187 'i': (0, "inactive"),
188 't': (2, "timeout"),
189 'T': (2, "timeout alarm"),
190 'A': (2, "alarm active"),
191 'L': (0, "alarm latched"),
192 '#': (0, "OK"),
195 if item in parsed["basic_components"]:
196 attrs = parsed["basic_components"][item]
197 state, state_readable = map_activation_states[attrs["activation"]]
198 return state, 'Activation status: %s, Type: %s' % \
199 (state_readable, attrs["type"])
202 check_info['emka_modules'] = {
203 'parse_function' : parse_emka_modules,
204 'inventory_function' : inventory_emka_modules,
205 'check_function' : check_emka_modules,
206 'service_description' : 'Module %s',
207 'snmp_info' : [('.1.3.6.1.4.1.13595.2.1.3.3.1', [
208 OID_END,
209 "3", # ELM2-MIB::basModuleStatus
210 "4", # ELM2-MIB::basModuleType
211 "5", # ELM2-MIB::basModuleInfo
212 "7", # ELM2-MIB::basModuleRemark
214 # In this order: alarm, handle, sensor, relay
215 ('.1.3.6.1.4.1.13595.2.2', [ '1', '2', '3', '4' ], [
216 OID_END,
217 "1.3", # ELM2-MIB::coHandleModuleLink
218 "1.4", # ELM2-MIB::co*[Status/Value]
219 "1.15", # ELM2-MIB::coSensorMode
221 ('.1.3.6.1.4.1.13595.2.2.3.1', [
222 OID_END,
223 "7", # ELM2-MIB::coSensorThreshold
225 ('.1.3.6.1.4.1.13595.2.2.3.1', [
226 OID_END,
227 BINARY("18"), # ELM2-MIB::coSensorScaling
228 ])],
229 'snmp_scan_function' : lambda oid: "emka" in oid(".1.3.6.1.2.1.1.1.0").lower() and \
230 oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.13595.1"),
234 # .--alarm---------------------------------------------------------------.
235 # | _ |
236 # | __ _| | __ _ _ __ _ __ ___ |
237 # | / _` | |/ _` | '__| '_ ` _ \ |
238 # | | (_| | | (_| | | | | | | | | |
239 # | \__,_|_|\__,_|_| |_| |_| |_| |
240 # | |
241 # '----------------------------------------------------------------------'
244 def inventory_emka_modules_alarm(parsed):
245 for entry, attrs in parsed.get("alarm", {}).items():
246 if attrs["value"] != "2":
247 yield entry, None
250 def check_emka_modules_alarm(item, params, parsed):
251 map_states = {
252 "1": (3, "unknown"),
253 "2": (0, "inactive"),
254 "3": (2, "active"),
255 "4": (0, "latched"),
258 if item in parsed.get("alarm", {}):
259 state, state_readable = map_states[parsed["alarm"][item]["value"]]
260 return state, "Status: %s" % state_readable
263 check_info["emka_modules.alarm"] = {
264 "inventory_function": inventory_emka_modules_alarm,
265 "check_function": check_emka_modules_alarm,
266 "service_description": "Alarm %s",
270 # .--handle--------------------------------------------------------------.
271 # | _ _ _ |
272 # | | |__ __ _ _ __ __| | | ___ |
273 # | | '_ \ / _` | '_ \ / _` | |/ _ \ |
274 # | | | | | (_| | | | | (_| | | __/ |
275 # | |_| |_|\__,_|_| |_|\__,_|_|\___| |
276 # | |
277 # '----------------------------------------------------------------------'
280 def inventory_emka_modules_handle(parsed):
281 for entry in parsed.get("handle", {}):
282 yield entry, None
285 def check_emka_modules_handle(item, params, parsed):
286 map_states = {
287 "1": (0, "closed"),
288 "2": (1, "opened"),
289 "3": (3, "unlocked"),
290 "4": (3, "delay"),
291 "5": (2, "open time ex")
294 if item in parsed.get("handle", {}):
295 state, state_readable = map_states[parsed["handle"][item]["value"]]
296 return state, "Status: %s" % state_readable
299 check_info["emka_modules.handle"] = {
300 "inventory_function": inventory_emka_modules_handle,
301 "check_function": check_emka_modules_handle,
302 "service_description": "Handle %s",
306 # .--voltage-------------------------------------------------------------.
307 # | _ _ |
308 # | __ _____ | | |_ __ _ __ _ ___ |
309 # | \ \ / / _ \| | __/ _` |/ _` |/ _ \ |
310 # | \ V / (_) | | || (_| | (_| | __/ |
311 # | \_/ \___/|_|\__\__,_|\__, |\___| |
312 # | |___/ |
313 # '----------------------------------------------------------------------'
316 def inventory_emka_modules_sensor_volt(parsed):
317 for entry in parsed.get("sensor_volt", {}):
318 yield entry, {}
321 def check_emka_modules_sensor_volt(item, params, parsed):
322 if item in parsed.get("sensor_volt", {}):
323 attrs = parsed["sensor_volt"][item]
324 value = attrs["value"] / 1000
325 return check_elphase(item, params, {item: {"voltage": value}})
328 check_info["emka_modules.sensor_volt"] = {
329 "inventory_function": inventory_emka_modules_sensor_volt,
330 "check_function": check_emka_modules_sensor_volt,
331 "service_description": "Phase %s",
332 "includes": ["elphase.include"],
333 "group": "el_inphase",
334 "has_perfdata": True,
338 # .--temperature---------------------------------------------------------.
339 # | _ _ |
340 # | | |_ ___ _ __ ___ _ __ ___ _ __ __ _| |_ _ _ _ __ ___ |
341 # | | __/ _ \ '_ ` _ \| '_ \ / _ \ '__/ _` | __| | | | '__/ _ \ |
342 # | | || __/ | | | | | |_) | __/ | | (_| | |_| |_| | | | __/ |
343 # | \__\___|_| |_| |_| .__/ \___|_| \__,_|\__|\__,_|_| \___| |
344 # | |_| |
345 # '----------------------------------------------------------------------'
348 def inventory_emka_modules_sensor_temp(parsed):
349 for entry in parsed.get("sensor_temp", {}):
350 yield entry, {}
353 def check_emka_modules_sensor_temp(item, params, parsed):
354 if item in parsed.get("sensor_temp", {}):
355 attrs = parsed["sensor_temp"][item]
356 value = attrs["value"]
357 return check_temperature(
358 value,
359 params,
360 "emka_modules_sensor_temp.%s" % item,
361 dev_levels=attrs["levels"],
362 dev_levels_lower=attrs["levels_lower"])
365 check_info["emka_modules.sensor_temp"] = {
366 "inventory_function": inventory_emka_modules_sensor_temp,
367 "check_function": check_emka_modules_sensor_temp,
368 "service_description": "Temperature %s",
369 "includes": ["temperature.include"],
370 "group": "temperature",
371 "has_perfdata": True,
375 # .--humidity------------------------------------------------------------.
376 # | _ _ _ _ _ |
377 # | | |__ _ _ _ __ ___ (_) __| (_) |_ _ _ |
378 # | | '_ \| | | | '_ ` _ \| |/ _` | | __| | | | |
379 # | | | | | |_| | | | | | | | (_| | | |_| |_| | |
380 # | |_| |_|\__,_|_| |_| |_|_|\__,_|_|\__|\__, | |
381 # | |___/ |
382 # '----------------------------------------------------------------------'
385 def inventory_emka_modules_sensor_humid(parsed):
386 for entry in parsed.get("sensor_humid", {}):
387 yield entry, {}
390 def check_emka_modules_sensor_humid(item, params, parsed):
391 if item in parsed.get("sensor_humid", {}):
392 attrs = parsed["sensor_humid"][item]
393 value = attrs["value"]
394 return check_humidity(value, params)
397 check_info["emka_modules.sensor_humid"] = {
398 "inventory_function": inventory_emka_modules_sensor_humid,
399 "check_function": check_emka_modules_sensor_humid,
400 "service_description": "Humidity %s",
401 "includes": ["humidity.include"],
402 "group": "humidity",
403 "has_perfdata": True,
407 # .--relay---------------------------------------------------------------.
408 # | _ |
409 # | _ __ ___| | __ _ _ _ |
410 # | | '__/ _ \ |/ _` | | | | |
411 # | | | | __/ | (_| | |_| | |
412 # | |_| \___|_|\__,_|\__, | |
413 # | |___/ |
414 # '----------------------------------------------------------------------'
417 def inventory_emka_modules_relay(parsed):
418 for entry, attrs in parsed.get("relay", {}).items():
419 if attrs["value"] != "1":
420 yield entry, None
423 def check_emka_modules_relay(item, params, parsed):
424 map_states = {
425 "1": (0, "off"),
426 "2": (0, "on"),
429 if item in parsed.get("relay", {}):
430 state, state_readable = map_states[parsed["relay"][item]["value"]]
431 return state, "Status: %s" % state_readable
434 check_info["emka_modules.relay"] = {
435 "inventory_function": inventory_emka_modules_relay,
436 "check_function": check_emka_modules_relay,
437 "service_description": "Relay %s",