diff --git a/plugins/snmp/snmp__synology b/plugins/snmp/snmp__synology new file mode 100755 index 00000000..20ec1f80 --- /dev/null +++ b/plugins/snmp/snmp__synology @@ -0,0 +1,188 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (C) 2014 Johann Schmitz +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +""" +=head1 NAME + +snmp__synology_ - Health and system status monitoring plugin for Synology NAS systems + +=head1 CONFIGURATION + +Make sure your Synology device is accessible via SNMP (e.g. via snmpwalk) and the munin-node +has been configured correctly. + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +Open a ticket at https://github.com/ercpe/contrib if you find one. + +=head1 AUTHOR + +Johann Schmitz + +=head1 LICENSE + +GPLv2 + +=cut +""" + +import re +import sys +import os +import logging + +from pysnmp.entity.rfc3413.oneliner import cmdgen + +disktable_id = '1.3.6.1.4.1.6574.2.1.1.2' +disktable_model = '1.3.6.1.4.1.6574.2.1.1.3' +disktable_temp = '1.3.6.1.4.1.6574.2.1.1.6' +sys_temperature = '1.3.6.1.4.1.6574.1.2.0' + +class SynologySNMPClient(object): + def __init__(self, host, port, community): + self.hostname = host + self.transport = cmdgen.UdpTransportTarget((host, int(port))) + self.auth = cmdgen.CommunityData('test-agent', community) + self.gen = cmdgen.CommandGenerator() + + def _get_disks(self): + disk_table = '1.3.6.1.4.1.6574.2.1' + errorIndication, errorStatus, errorIndex, varBindTable = self.gen.bulkCmd( + self.auth, + self.transport, + 0, 24, + disk_table) + + if errorIndication: + logging.error("SNMP bulkCmd for devices failed: %s, %s, %s" % (errorIndication, errorStatus, errorIndex)) + return + + devices = {} + for row in varBindTable: + for oid, value in row: + oid = str(oid) + if not oid.startswith(disk_table): + continue + + disk_id = oid[oid.rindex('.')+1:] + + values = devices.get(disk_id, [None, None, None]) + if oid.startswith(disktable_id): + values[0] = str(value).strip() + if oid.startswith(disktable_model): + values[1] = str(value).strip() + if oid.startswith(disktable_temp): + values[2] = int(value) + devices[disk_id] = values + + for x in sorted(devices.keys()): + yield tuple([x] + devices[x]) + + def _get_sys_temperature(self): + errorIndication, errorStatus, errorIndex, varBindTable = self.gen.getCmd( + self.auth, + self.transport, + sys_temperature) + + if errorIndication: + logging.error("SNMP getCmd for %s failed: %s, %s, %s" % (sys_temperature, errorIndication, errorStatus, errorIndex)) + return None + + return int(varBindTable[0][1]) + + def print_config(self): + print """multigraph synology_hdd_temperatures +host_name {hostname} +graph_title HDD temperatures on {hostname} +graph_vlabel Temperature in °C +graph_args --base 1000 +graph_category system +graph_info HDD temperatures on {hostname}""".format(hostname=self.hostname) + + for id, name, model, temp in self._get_disks(): + print """disk{disk_id}.info Temperature of {name} ({model}) +disk{disk_id}.label {name} ({model}) +disk{disk_id}.type GAUGE +disk{disk_id}.min 0""".format(disk_id=id, name=name, model=model) + + + print """multigraph synology_sys_temperature +host_name {hostname} +graph_title System temperatures of {hostname} +graph_vlabel Temperature in °C +graph_args --base 1000 +graph_category system +graph_info System temperature of {hostname} +sys_temp.info System temperature +sys_temp.label Temperature +sys_temp.type GAUGE +sys_temp.min 0 +""".format(hostname=self.hostname) + + def execute(self): + print """multigraph synology_hdd_temperatures""" + for id, name, model, temp in self._get_disks(): + print """disk{disk_id}.value {temp}""".format(disk_id=id, temp=temp) + + print """multigraph synology_sys_temperature""" + print "sys_temp.value {temp}".format(temp=self._get_sys_temperature()) + + +host = None +port = os.getenv('port', 161) +community = os.getenv('community', None) +debug = bool(os.getenv('MUNIN_DEBUG', os.getenv('DEBUG', 0))) + +if debug: + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-7s %(message)s') + +try: + match = re.search("^(?:|.*\/)snmp_([^_]+)_synology$", sys.argv[0]) + host = match.group(1) + match = re.search("^([^:]+):(\d+)$", host) + if match is not None: + host = match.group(1) + port = match.group(2) +except Exception as ex: + logging.error("Caught exception: %s" % ex) + + +if "snmpconf" in sys.argv[1:]: + print "require 1.3.6.1.4.1.6574.2.1.1" + sys.exit(0) +else: + if not (host and port and community): + print "# Bad configuration. Cannot run with Host=%s, port=%s and community=%s" % (host, port, community) + sys.exit(1) + + c = SynologySNMPClient(host, port, community) + + if "config" in sys.argv[1:]: + c.print_config() + else: + c.execute()