Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / disk / snmp__areca_ @ 4b400a73

Historique | Voir | Annoter | Télécharger (7,65 ko)

1
#!/usr/bin/python
2

    
3
# Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU Library General Public License as published by
7
# the Free Software Foundation; version 2 only
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Library General Public License for more details.
13
#
14
# You should have received a copy of the GNU Library General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
#
18

    
19
"""
20
=head1 NAME
21

    
22
snmp__areca_ - Munin plugin to get temperature, voltage or fan speed values
23
from Areca network enabled RAID controllers via SNMP.
24

    
25
=head1 APPLICABLE SYSTEMS
26

    
27
All machines with a SNMP capable ARECA raid controller.
28

    
29
=head1 CONFIGURATION
30

    
31
Make sure your Areca controller is accessible via SNMP from the munin host:
32
snmpwalk -v 1 -c snmp_community ip.add.re.ss
33

    
34
The plugin is a wildcard plugin and can thus be used to retrieve different
35
values depending on the name of the file.
36

    
37
Linking it as snmp_10.8.1.230_areca_fan would retrieve the fan speed values from
38
the Areca controller at 10.8.1.230.
39

    
40
Valid values are fan, temp and volt.
41

    
42
Add the following to your /etc/munin/plugin-conf.d/snmp__areca file:
43

    
44
 [snmp_ip.add.re.ss_*]
45
 community private
46
 version 1
47

    
48
Then test the plugin by calling the following commands:
49

    
50
munin-run snmp_10.8.1.230_areca_temp config
51
munin-run snmp_10.8.1.230_areca_temp
52

    
53
Both commands should output sensible data without failing.
54

    
55
=head1 INTERPRETATION
56

    
57
The plugin shows the temperature in Celsius or the fanspeed in rotations per minute.
58

    
59
=head1 MAGIC MARKERS
60

    
61
  #%# family=contrib
62
  #%# capabilities=
63

    
64
=head1 VERSION
65

    
66
0.0.1
67

    
68
=head1 BUGS
69

    
70
None known. If you know of any, please raise a ticket at https://trac.bawue.org/munin/wiki/areca__snmp_
71

    
72
=head1 AUTHOR
73

    
74
Andreas Thienemann <andreas@bawue.net>
75

    
76
=head1 LICENSE
77

    
78
GPLv2
79

    
80
=cut
81
"""
82

    
83
import pprint
84
import time
85
import sys
86
import re
87
import os
88
from pysnmp import v1, v2c, role, asn1
89

    
90
request_conf = {
91
	"volt" : {
92
		"label"  : "Voltages",
93
		"vlabel" : "Volt",
94
		"graph"  : "--base 1000 --logarithmic",
95
		"oid"    : ".1.3.6.1.4.1.18928.1.2.2.1.8"
96
	},
97
	"fan"  : {
98
		"label"  : "Fans",
99
		"vlabel" : "RPM",
100
		"graph"  : "--base 1000 -l 0",
101
		"oid"    : ".1.3.6.1.4.1.18928.1.2.2.1.9"
102
	},
103
	"temp" : {
104
		"label"  : "Temperatures",
105
		"vlabel" : "Celsius",
106
		"graph"  : "--base 1000 -l 0",
107
		"oid"   : ".1.3.6.1.4.1.18928.1.2.2.1.10"
108
	}
109
}
110

    
111
# Sanity check and parsing of the commandline
112
host = None
113
port = 161
114
request = None
115
try:
116
	match = re.search("^(?:|.*\/)snmp_([^_]+)_areca_(.+)$", sys.argv[0])
117
	host = match.group(1)
118
	request =  match.group(2)
119
	match = re.search("^([^:]+):(\d+)$", host)
120
	if match is not None:
121
		host = match.group(1)
122
		port = match.group(2)
123
except:
124
	pass
125

    
126
if host is None or request is None:
127
        print "# Error: Incorrect filename. Cannot parse host or request."
128
	sys.exit(1)
129

    
130
# Parse env variables
131
if os.getenv("community") is not None:
132
	community = os.getenv("community")
133
else:
134
	community = "public"
135
if os.getenv("version") is not None:
136
	version = os.getenv("version")
137
else:
138
	version = "1"
139

    
140

    
141
def get_data():
142
	# Fetch the data
143
	results = snmpwalk(request_conf[request]["oid"])
144

    
145
	# parse data
146
	vals = []
147
	for i in range(0, len(results)):
148
		idx, res = results[i][0].split(request_conf[request]["oid"])[1].split(".")[1:], results[i][1]
149
		if idx[1] == '1':
150
			vals.append([])
151
			vals[int(idx[2])-1].append(res)
152
		if idx[1] == '2':
153
			vals[int(idx[2])-1].append(res)
154
		if idx[1] == '3':
155
			if request == "volt":
156
				res = float(res)/1000
157
			vals[int(idx[2])-1].append(res)
158

    
159
	return vals
160

    
161
def snmpwalk(root):
162

    
163
	# Create SNMP manager object
164
	client = role.manager((host, port))
165

    
166
	# Create a SNMP request&response objects from protocol version
167
	# specific module.
168
	try:
169
		req = eval('v' + version).GETREQUEST()
170
		nextReq = eval('v' + version).GETNEXTREQUEST()
171
		rsp = eval('v' + version).GETRESPONSE()
172

    
173
	except (NameError, AttributeError):
174
		print '# Unsupported SNMP protocol version: %s\n%s' % (version, usage)
175
		sys.exit(-1)
176

    
177
	# Store tables headers
178
	head_oids = [root]
179

    
180
	encoded_oids = map(asn1.OBJECTID().encode, head_oids)
181

    
182
	result = [];
183

    
184
	while 1:
185

    
186
		# Encode OIDs, encode SNMP request message and try to send
187
		# it to SNMP agent and receive a response
188
		(answer, src) = client.send_and_receive(req.encode(community=community, encoded_oids=encoded_oids))
189

    
190
		# Decode SNMP response
191
		rsp.decode(answer)
192
        
193
		# Make sure response matches request (request IDs, communities, etc)
194
		if req != rsp:
195
			raise 'Unmatched response: %s vs %s' % (str(req), str(rsp))
196

    
197
		# Decode BER encoded Object IDs.
198
		oids = map(lambda x: x[0], map(asn1.OBJECTID().decode, rsp['encoded_oids']))
199

    
200
		# Decode BER encoded values associated with Object IDs.
201
		vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals']))
202

    
203
		# Check for remote SNMP agent failure
204
		if rsp['error_status']:
205
 			# SNMP agent reports 'no such name' when walk is over
206
			if rsp['error_status'] == 2:
207
				# Switch over to GETNEXT req on error
208
				# XXX what if one of multiple vars fails?
209
				if not (req is nextReq):
210
					req = nextReq
211
					continue
212
				# One of the tables exceeded
213
				for l in oids, vals, head_oids:
214
					del l[rsp['error_index']-1]
215
			else:
216
				raise 'SNMP error #' + str(rsp['error_status']) + ' for OID #' + str(rsp['error_index'])
217

    
218
		# Exclude completed OIDs
219
		while 1:
220
			for idx in range(len(head_oids)):
221
				if not asn1.OBJECTID(head_oids[idx]).isaprefix(oids[idx]):
222
					# One of the tables exceeded
223
					for l in oids, vals, head_oids:
224
						del l[idx]
225
					break
226
			else:
227
				break
228

    
229
		if not head_oids:
230
			return result
231

    
232
		# Print out results
233
		for (oid, val) in map(None, oids, vals):
234
			result.append((oid, str(val)))
235
			# print oid + ' ---> ' + str(val)
236

    
237
		# BER encode next SNMP Object IDs to query
238
		encoded_oids = map(asn1.OBJECTID().encode, oids)
239

    
240
		# Update request object
241
		req['request_id'] = req['request_id'] + 1
242

    
243
		# Switch over GETNEXT PDU for if not done
244
		if not (req is nextReq):
245
			req = nextReq
246

    
247
	raise "error"
248

    
249

    
250
def print_config():
251
	print "graph_title " + request_conf[request]["label"]
252
	print "graph_vlabel " + request_conf[request]["vlabel"]
253
	print "graph_args " + request_conf[request]["graph"]
254
	print "graph_category sensors"
255
	print "host_name", host
256
	for dataset in get_data():
257
		if request == "volt":
258
			if dataset[1] == "Battery Status":
259
				continue
260
			else:
261
				print request + dataset[0] + ".label", dataset[1]
262
				ref_val = float(dataset[1].split()[-1][:-1])
263
				print request + dataset[0] + ".warning", str(ref_val * 0.95) + ":" + str(ref_val * 1.05)
264
				print request + dataset[0] + ".critical", str(ref_val * 0.80) + ":" + str(ref_val * 1.20)
265
		if request == "temp":
266
			print request + dataset[0] + ".label", dataset[1]
267
			if dataset[1].startswith("CPU"):
268
				print request + dataset[0] + ".warning", 55
269
				print request + dataset[0] + ".critical", 60
270
			if dataset[1].startswith("System"):
271
				print request + dataset[0] + ".warning", 40
272
				print request + dataset[0] + ".critical", 45
273
		if request == "fan":
274
			print request + dataset[0] + ".label", dataset[1]
275
			print request + dataset[0] + ".warning", 2400
276
			print request + dataset[0] + ".critical", 2000
277

    
278
	sys.exit(0)
279

    
280

    
281
if "config" in sys.argv[1:]:
282
	print_config()
283
elif "snmpconf" in sys.argv[1:]:
284
	print "require 1.3.6.1.4.1.18928.1.2.2.1.8.1.1"
285
	sys.exit(0)
286
else:
287
	for dataset in get_data():
288
		# Filter Battery Status (255 == Not installed)
289
		if request == "volt" and dataset[1] == "Battery Status":
290
			continue
291
		print request + dataset[0] + ".value", dataset[2]
292
	sys.exit(0)