Index: /snmp__areca_ =================================================================== --- /snmp__areca_ (revision 6) +++ /snmp__areca_ (revision 6) @@ -0,0 +1,225 @@ +#!/usr/bin/python + +# Copyright (C) 2009 Andreas Thienemann +# +# 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. +# + +# +# Munin Plugin to get Temperature, Voltage or Fanspeed from Areca Controllers +# via SNMP. +# +# Parameters: +# +# config (required) +# autoconf (optional - only used by munin-config) +# + +# Magic markers (optional - only used by munin-config and some +# installation scripts): + +import pprint +import time +import sys +import re +import os +from pysnmp import v1, v2c, role, asn1 + +request_conf = { + "volt" : { + "label" : "Voltages", + "vlabel" : "Volt", + "graph" : "--base 1000 --logarithmic", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.8" + }, + "fan" : { + "label" : "Fans", + "vlabel" : "RPM", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.9" + }, + "temp" : { + "label" : "Temperatures", + "vlabel" : "Celsius", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.10" + } +} + +# Sanity check and parsing of the commandline +host = None +port = 161 +request = None +try: + match = re.search("^(?:|.*\/)snmp_([^_]+)_areca_(.+)$", sys.argv[0]) + host = match.group(1) + request = match.group(2) + match = re.search("^([^:]+):(\d+)$", host) + if match is not None: + host = match.group(1) + port = match.group(2) +except: + pass + +if host is None or request is None: + print "# Error: Incorrect filename. Cannot parse host or request." + sys.exit(1) + +# Parse env variables +if os.getenv("community") is not None: + community = os.getenv("community") +else: + community = "public" +if os.getenv("version") is not None: + version = os.getenv("version") +else: + version = "1" + + +def get_data(): + # Fetch the data + results = snmpwalk(request_conf[request]["oid"]) + + # parse data + vals = [] + for i in range(0, len(results)): + idx, res = results[i][0].split(request_conf[request]["oid"])[1].split(".")[1:], results[i][1] + if idx[1] == '1': + vals.append([]) + vals[int(idx[2])-1].append(res) + if idx[1] == '2': + vals[int(idx[2])-1].append(res) + if idx[1] == '3': + if request == "volt": + res = float(res)/1000 + vals[int(idx[2])-1].append(res) + + return vals + +def snmpwalk(root): + + # Create SNMP manager object + client = role.manager((host, port)) + + # Create a SNMP request&response objects from protocol version + # specific module. + try: + req = eval('v' + version).GETREQUEST() + nextReq = eval('v' + version).GETNEXTREQUEST() + rsp = eval('v' + version).GETRESPONSE() + + except (NameError, AttributeError): + print '# Unsupported SNMP protocol version: %s\n%s' % (version, usage) + sys.exit(-1) + + # Store tables headers + head_oids = [root] + + encoded_oids = map(asn1.OBJECTID().encode, head_oids) + + result = []; + + while 1: + + # Encode OIDs, encode SNMP request message and try to send + # it to SNMP agent and receive a response + (answer, src) = client.send_and_receive(req.encode(community=community, encoded_oids=encoded_oids)) + + # Decode SNMP response + rsp.decode(answer) + + # Make sure response matches request (request IDs, communities, etc) + if req != rsp: + raise 'Unmatched response: %s vs %s' % (str(req), str(rsp)) + + # Decode BER encoded Object IDs. + oids = map(lambda x: x[0], map(asn1.OBJECTID().decode, rsp['encoded_oids'])) + + # Decode BER encoded values associated with Object IDs. + vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals'])) + + # Check for remote SNMP agent failure + if rsp['error_status']: + # SNMP agent reports 'no such name' when walk is over + if rsp['error_status'] == 2: + # Switch over to GETNEXT req on error + # XXX what if one of multiple vars fails? + if not (req is nextReq): + req = nextReq + continue + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[rsp['error_index']-1] + else: + raise 'SNMP error #' + str(rsp['error_status']) + ' for OID #' + str(rsp['error_index']) + + # Exclude completed OIDs + while 1: + for idx in range(len(head_oids)): + if not asn1.OBJECTID(head_oids[idx]).isaprefix(oids[idx]): + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[idx] + break + else: + break + + if not head_oids: + return result + + # Print out results + for (oid, val) in map(None, oids, vals): + result.append((oid, str(val))) + # print oid + ' ---> ' + str(val) + + # BER encode next SNMP Object IDs to query + encoded_oids = map(asn1.OBJECTID().encode, oids) + + # Update request object + req['request_id'] = req['request_id'] + 1 + + # Switch over GETNEXT PDU for if not done + if not (req is nextReq): + req = nextReq + + raise "error" + + +def print_config(): + print "graph_title " + request_conf[request]["label"] + print "graph_vlabel " + request_conf[request]["vlabel"] + print "graph_args " + request_conf[request]["graph"] + print "graph_category sensors" + print "graph_order", + print + print "host_name", host + for dataset in get_data(): + if request == "volt" and dataset[1] == "Battery Status": + continue + print request + dataset[0] + ".label", dataset[1] + sys.exit(0) + + +if "config" in sys.argv[1:]: + print_config() +elif "snmpconf" in sys.argv[1:]: + print "require 1.3.6.1.4.1.18928.1.2.2.1.8.1.1" + sys.exit(0) +else: + for dataset in get_data(): + # Filter Battery Status (255 == Not installed) + if request == "volt" and dataset[1] == "Battery Status": + continue + print request + dataset[0] + ".value", dataset[2] + sys.exit(0)