commit 594331e987490bd3a9e3911764e93999751abe6c Author: Alexander Reinert Date: Sun Jun 25 21:24:20 2017 +0200 Initial commit diff --git a/addon/addon/common.tcl b/addon/addon/common.tcl new file mode 100644 index 0000000..8ee695b --- /dev/null +++ b/addon/addon/common.tcl @@ -0,0 +1,55 @@ +set ADDON_NAME "check_mk_agent" + +proc log { message } { + global ADDON_NAME + exec logger "$ADDON_NAME - $message" +} + +proc save_to_file { fileName content } { + set fd -1 + + set fd [open $fileName w] + if { $fd != -1 } then { + puts -nonewline $fd $content + close $fd + } else { + error "could not write file $fileName" + } +} + +proc load_from_file { fileName } { + set fd -1 + + set fd [open $fileName r] + if { $fd != -1 } then { + set result [read $fd] + close $fd + } else { + error "could not read file $fileName" + } + + return $result +} + +set PID_FILE "/var/lock/$ADDON_NAME.pid" + +proc is_running { } { + global PID_FILE + return [file exists $PID_FILE] +} + +proc write_pid_file { } { + global PID_FILE + save_to_file $PID_FILE [pid] +} + +proc read_pid_file { } { + global PID_FILE + return [load_from_file $PID_FILE] +} + +proc remove_pid_file { } { + global PID_FILE + file delete $PID_FILE +} + diff --git a/addon/addon/server.tcl b/addon/addon/server.tcl new file mode 100644 index 0000000..15b1308 --- /dev/null +++ b/addon/addon/server.tcl @@ -0,0 +1,178 @@ +#!/bin/tclsh +load tclrega.so +load tclrpc.so + +source [file join [file dirname [info script]] common.tcl] + +proc handle_connection { channelId clientAddress clientPort } { + if { [catch { + log "connection accepted from $clientAddress:$clientPort" + + puts $channelId "<<>>" + puts $channelId "Version: [get_version]" + puts $channelId "AgentOS: HomeMatic" + puts $channelId "Hostname: [info hostname]" + + puts $channelId "<<>>" + puts $channelId [string trim [load_from_file /proc/meminfo]] + + puts $channelId "<<>>" + puts $channelId "[string trim [load_from_file /proc/loadavg]] [exec grep -E ^(P|p)rocessor < /proc/cpuinfo | wc -l]" + + puts $channelId "<<>>" + puts $channelId [string trim [load_from_file /proc/uptime]] + + puts $channelId "<<>>" + puts $channelId [clock seconds] + puts $channelId [string trim [load_from_file /proc/vmstat]] + puts $channelId [string trim [load_from_file /proc/stat]] + + if { [file exists /sys/class/thermal/thermal_zone0/temp] == 1} { + puts $channelId "<<>>" + puts $channelId "thermal_zone0 enabled [string trim [load_from_file /sys/class/thermal/thermal_zone0/type]] [string trim [load_from_file /sys/class/thermal/thermal_zone0/temp]]" + } + + if { [file exists /proc/net/tcp6] == 1 } { + puts $channelId "<<>>" + puts $channelId "[exec cat /proc/net/tcp /proc/net/tcp6 2>/dev/null | awk { /:/ { c[$4]++; } END { for (x in c) { print x, c[x]; } } }]" + } else { + puts $channelId "<<>>" + puts $channelId "[exec cat /proc/net/tcp 2>/dev/null | awk { /:/ { c[$4]++; } END { for (x in c) { print x, c[x]; } } }]" + } + + puts $channelId "<<>>" + puts $channelId "\[start_iplink\]" + puts $channelId "[exec ip link]" + puts $channelId "\[end_iplink\]" + + puts $channelId "<<>>" + puts $channelId "[exec sed 1,2d /proc/net/dev]" + + if { [regexp CCU2 [exec grep Hardware < /proc/cpuinfo]] == 0 } { + puts $channelId "<<>>" + puts $channelId "[exec df -PTk | sed 1d]" + + puts $channelId "<<>>" + puts $channelId "[exec grep ^/dev < /proc/mounts]" + + puts $channelId "<<>>" + puts $channelId "[clock seconds]" + puts $channelId "[exec egrep { (x?[shv]d[a-z]*|cciss/c[0-9]+d[0-9]+|emcpower[a-z]+|dm-[0-9]+|VxVM.*|mmcblk.*|dasd[a-z]*|bcache[0-9]+|nvme[0-9]+n[0-9]+) } < /proc/diskstats]" + } + + if { [file exists /usr/bin/ntpq] == 1 } { + puts $channelId "<<>>" + puts $channelId "[exec ntpq -np | sed -e 1,2d -e {s/^\(.\)/\1 /} -e {s/^ /%/}]" + } + + puts $channelId "<<>>" + puts $channelId [string trim [get_homematic_check_result]] + foreach dev [xmlrpc http://127.0.0.1:2001/ listBidcosInterfaces] { + foreach {key value} [split $dev] { + set values($key) $value + } + set address $values(ADDRESS) + foreach key [array names values] { + if { $key != "ADDRESS" } { + puts $channelId "$address;$key;$values($key)" + } + } + } + + flush $channelId + + close $channelId + } err] } { + log $err + [catch { close $channelId }] + } +} + +proc get_homematic_check_result { } { + array set result [rega_script { + string _svcId; + object _svc; + string _devId; + object _dev; + string _chId; + object _ch; + string _dpId; + object _dp; + string _name; + + foreach (_svcId, dom.GetObject(ID_SERVICES).EnumUsedIDs()) { + _svc = dom.GetObject(_svcId); + if (_svc.AlState() == asOncoming) { + _dp = dom.GetObject(_svc.AlTriggerDP()); + _ch = dom.GetObject(_dp.Channel()); + _dev = dom.GetObject(_ch.Device()); + + WriteLine("SVC_MSG;" # _dev.Name() # ";" # _svc.Name().StrValueByIndex (".", 1).StrValueByIndex ("-", 0) # ";" # _dp.Timestamp()); + } + } + + foreach (_chId, dom.GetObject("Monitored").EnumUsedIDs()) { + _ch = dom.GetObject(_chId); + + _dev = dom.GetObject(_ch.Device()); + WriteLine(_ch.Name() # ";HSSTYPE;" # _dev.HssType()); + + foreach (_dpId, _ch.DPs()) { + _dp = dom.GetObject(_dpId); + + if (_dp.Value()) { + _name = _dp.Name().StrValueByIndex(".", 2); + WriteLine(_ch.Name() # ";" # _name # ";" # _dp.Value() # ";" # _dp.Timestamp()); + } + } + } + }] + return $result(STDOUT) +} + +proc get_homematic_bidcos_devices { } { + foreach dev [xmlrpc http://127.0.0.1:2001/ listBidcosInterfaces] { + foreach {key value} [split $dev] { + puts "$key=$value" + } + } +} + +proc read_var { filename varname } { + set fd [open $filename r] + set var "" + if { $fd >=0 } { + while { [gets $fd buf] >=0 } { + if [regexp "^ *$varname *= *(.*)$" $buf dummy var] break + } + close $fd + } + return $var +} + +proc get_version { } { + return [read_var /boot/VERSION VERSION] +} + +proc main { } { + startup + + socket -server handle_connection 6556 + log "check_mk agent started and waiting for connections..." + + vwait forever +} + +proc startup { } { + if {[is_running]} then { + error "already running" + } + + write_pid_file +} + +if { [catch { main } err] } then { + log $err + exit 1 +} + diff --git a/addon/addon/stop.tcl b/addon/addon/stop.tcl new file mode 100644 index 0000000..72493d6 --- /dev/null +++ b/addon/addon/stop.tcl @@ -0,0 +1,20 @@ +#!/bin/tclsh + +source [file join [file dirname [info script]] common.tcl] + +proc main { } { + if { [is_running] } then { + set pid [read_pid_file] + + catch { exec kill -KILL $pid } + remove_pid_file + } + + log "stopped" +} + +if { [catch { main } errorMessage] } then { + log $errorMessage + exit 1 +} + diff --git a/addon/rc.d/check_mk_agent b/addon/rc.d/check_mk_agent new file mode 100644 index 0000000..a063fbe --- /dev/null +++ b/addon/rc.d/check_mk_agent @@ -0,0 +1,47 @@ +#!/bin/sh + +ADDONNAME=check_mk_agent +ADDONDIR=/usr/local/addons/${ADDONNAME} +#WWWDIR=/usr/local/etc/config/addons/www/${ADDONNAME} +RCDDIR=/usr/local/etc/config/rc.d + +case "$1" in + + ""|start) + tclsh $ADDONDIR/server.tcl & + ;; + + stop) + tclsh $ADDONDIR/stop.tcl + ;; + + restart|reload) + tclsh $ADDONDIR/stop.tcl + sleep 2 + tclsh $ADDONDIR/server.tcl & + ;; + + info) + echo "Info: (Inoffical) check_mk agent" + echo "Version: 1.0" + echo "Name: check_mk_agent" + echo "Operations: uninstall restart" + ;; + + uninstall) + tclsh $ADDONDIR/stop.tcl + + rm -rf ${ADDONDIR} + rm -rf ${WWWDIR} + rm -f ${RCDDIR}/${ADDONNAME} + ;; + + *) + echo "Usage: check_mk_agent {start|stop|restart|info|uninstall}" >&2 + exit 1 + ;; + +esac + +exit $? + diff --git a/addon/update_script b/addon/update_script new file mode 100644 index 0000000..833033c --- /dev/null +++ b/addon/update_script @@ -0,0 +1,22 @@ +#!/bin/sh + +ADDONNAME=check_mk_agent +CONFIG_DIR=/usr/local/etc/config +ADDON_DIR=/usr/local/addons/${ADDONNAME} +RCD_DIR=${CONFIG_DIR}/rc.d + +mount | grep /usr/local 2>&1 >/dev/null +if [ $? -eq 1 ]; then + mount /usr/local +fi + +# create necessary directories +mkdir -p ${ADDON_DIR} +chmod 755 ${ADDON_DIR} +mkdir -p ${RCD_DIR} +chmod 755 ${RCD_DIR} + +cp -af addon/* ${ADDON_DIR} +cp -af rc.d/* ${RCD_DIR} + +sync diff --git a/check/checks/homematic b/check/checks/homematic new file mode 100644 index 0000000..f06a91d --- /dev/null +++ b/check/checks/homematic @@ -0,0 +1,167 @@ +#!/usr/bin/python + +def parse_homematic(info): + result = { } + for line in info: + key = line[0] + device = result.get(key) + if key == 'SVC_MSG': + if device is None: + device = [] + result[key] = device + device.append(line[1:]) + else: + if device is None: + device = { } + result[key] = device + device[line[1]] = line[2:] + return result + +def inventory_homematic(parsed): + yield 'Low Battery devices', None + yield 'Unreachable devices', None + yield 'Service Messages', None + +def check_homematic(item, params, parsed): + messages = parsed.get('SVC_MSG', None); + + state = 0 + devices = [] + + if messages is not None: + for msg in messages: + if item == 'Low Battery devices': + if msg[1] == 'LOWBAT': + state = 2 + devices.append(msg[0]) + elif item == 'Unreachable devices': + if msg[1] == 'UNREACH': + state = 2 + devices.append(msg[0]) + elif msg[1] == 'STICKY_UNREACH': + state = max(state, 1) + devices.append(msg[0]) + elif item == 'Service Messages': + if msg[1] not in [ 'LOWBAT', 'UNREACH', 'STICKY_UNREACH' ]: + if msg[1] in [ 'CONFIG_PENDING', 'DEVICE_IN_BOOTLOADER', 'UPDATE_PENDING', 'USBH_POWERFAIL' ]: + state = max(state, 1) + else: + state = 2 + devices.append(msg[0] + ':' + msg[1]) + + if state == 0: + return 0, 'No issues reported' + + return state, ', '.join(devices) + +check_info['homematic'] = { + 'check_function': check_homematic, + 'inventory_function': inventory_homematic, + 'parse_function': parse_homematic, + 'service_description': "Homematic %s", +} + +def inventory_homematic_humidity(parsed): + for line in parsed: + data = parsed[line] + if isinstance(data, dict): + temperature = data.get('TEMPERATURE', data.get('ACTUAL_TEMPERATURE', None)) + humidity = data.get('HUMIDITY', None) + + if temperature is not None and humidity is not None: + yield line, { } + +# data = parsed[line] +# if isinstance(data, dict) and data.get('HSSTYPE', 'UNKNOWN') in [ [u'HM-WDS10-TH-O'], [u'HM-WDS20-TH-O'], [u'HM-WDS40-TH-I'], [u'HM-CC-TC'], [u'HM-WDS100-C6-O-2'], [u'HM-WDS100-C6-O'], [u'HM-TC-IT-WM-W-EU'], [u'HM-WDS40-TH-I-2'], [u'HM-WDC7000'], [u'HMIP-WTH'], [u'HmIP-WTH-2'], [u'HmIP-STH'], [u'HmIP-STHD'], [u'HmIP-BWTH'], [u'HmIP-BWTH24'] ]: +# yield line, { } + +def check_homematic_humidity(item, params, parsed): + for line in parsed: + if line == item: + data = parsed[line] + temperature = data.get('TEMPERATURE', data.get('ACTUAL_TEMPERATURE', None)) + humidity = data.get('HUMIDITY', None) + + if humidity is None or temperature is None: + return 3, 'Humidity or temperature are not available' + + temperature = float(temperature[0]) + humidity = float(humidity[0]) + + if humidity <= params['critical'][0] or humidity >= params['critical'][1]: + state = 2 + elif humidity <= params['warning'][0] or humidity >= params['warning'][1]: + state = 1 + else: + state = 0 + + message = "Temperature: %f, Humidity: %f" % (temperature, humidity) + perfdata = [ + ('temperature', temperature), + ('humidity', humidity), + ] + + return state, message, perfdata + +factory_settings['homematic_humidity_default_levels'] = { + 'warning' : (40, 60), + 'critical': (35, 65), +} + +homematic_humidity_default_levels = { } + +check_info['homematic.humidity'] = { + 'check_function': check_homematic_humidity, + 'inventory_function': inventory_homematic_humidity, + 'service_description': "Humidity %s", + 'group': 'homematic_humidity', + 'default_levels_variable': 'homematic_humidity_default_levels', + 'has_perfdata': True, +} + +def inventory_homematic_dutycycle(parsed): + for line in parsed: + data = parsed[line] + if isinstance(data, dict) and data.get('DUTY_CYCLE', None) is not None: + yield line, { } + +def check_homematic_dutycycle(item, params, parsed): + for line in parsed: + if line == item: + data = parsed[line] + dutycycle = data.get('DUTY_CYCLE', None) + + if dutycycle is None : + return 3, 'Duty cycle is not available' + + dutycycle = float(dutycycle[0]) + if dutycycle >= params['critical']: + state = 2 + elif dutycycle >= params['warning']: + state = 1 + else: + state = 0 + + message = "Duty cycle: %f" % (dutycycle) + perfdata = [ + ('dutycycle', dutycycle, params['warning'], params['critical'], 0, 100), + ] + + return state, message, perfdata + +factory_settings['homematic_dutycycle_default_levels'] = { + 'warning' : 50, + 'critical': 70, +} + +homematic_dutycycle_default_levels = { } + +check_info['homematic.dutycycle'] = { + 'check_function': check_homematic_dutycycle, + 'inventory_function': inventory_homematic_dutycycle, + 'service_description': "Duty cycle %s", + 'group': 'homematic_dutycycle', + 'default_levels_variable': 'homematic_dutycycle_default_levels', + 'has_perfdata': True, +} + diff --git a/check/homematic b/check/homematic new file mode 100644 index 0000000..3077f9b --- /dev/null +++ b/check/homematic @@ -0,0 +1,20 @@ +{'author': 'Alexander Reinert ', + 'description': 'Homematic device checks to use with the CCU check_mk_agent addon', + 'download_url': 'https://github.com/alexreinert/homematic_check_mk', + 'files': {'agents': [], + 'bin': [], + 'checkman': [], + 'checks': ['homematic'], + 'doc': [], + 'inventory': [], + 'lib': [], + 'mibs': [], + 'notifications': [], + 'pnp-templates': [], + 'web': ['plugins/perfometer/homematic.py', + 'plugins/wato/homematic.py']}, + 'name': 'homematic', + 'title': 'Homematic', + 'version': '1.0', + 'version.min_required': '1.2.8', + 'version.packaged': '1.4.0p5'} diff --git a/check/web/plugins/perfometer/homematic.py b/check/web/plugins/perfometer/homematic.py new file mode 100644 index 0000000..edc7a0c --- /dev/null +++ b/check/web/plugins/perfometer/homematic.py @@ -0,0 +1,9 @@ +def perfometer_check_mk_homematic_dutycycle(row, check_command, perf_data): + dutycycle = float(perf_data[0][1]) + state = row["service_state"] + color = { 0: "#39f", 1: "#ff2", 2: "#f22", 3: "#fa2" }[state] + + return "%.0f%%" % dutycycle, perfometer_linear(dutycycle, color) + +perfometers["check_mk-homematic.dutycycle"] = perfometer_check_mk_homematic_dutycycle + diff --git a/check/web/plugins/wato/homematic.py b/check/web/plugins/wato/homematic.py new file mode 100644 index 0000000..04f6a00 --- /dev/null +++ b/check/web/plugins/wato/homematic.py @@ -0,0 +1,59 @@ +checkgroups = [] +subgroup_os = _("Temperature, Humidity, Electrical Parameters, etc.") + +register_check_parameters( + subgroup_os, + "homematic_humidity", + _("Homematic Humidity"), + Dictionary( + title = _("Parameters for the Homematic Humidity check"), + help = _(""), + elements = [ + ( "warning", + Tuple( + title = _("Warning level of humidity"), + elements = [ + Integer(title = _("Lower than"), default_value = 40), + Integer(title = _("Higher than"), default_value = 60), + ], + ), + ), + ( "critical", + Tuple( + title = _("Critical level of humidity"), + elements = [ + Integer(title = _("Lower than"), default_value = 30), + Integer(title = _("Higher than"), default_value = 70), + ], + ), + ), + ], + ), + TextAscii( + title = _("Name of the device"), + ), + match_type = "dict", +) + +register_check_parameters( + subgroup_os, + "homematic_dutycycle", + _("Homematic Duty cycle"), + Dictionary( + title = _("Parameters for the Homematic Duty cycle check"), + help = _(""), + elements = [ + ( "warning", + Integer(title = _("Warning level of duty cycle"), default_value = 50) + ), + ( "critical", + Integer(title = _("Critical level of duty cycle"), default_value = 70) + ), + ], + ), + TextAscii( + title = _("Name of the device"), + ), + match_type = "dict", +) +