snmpgetをつかって定期的にそこのインターフェースに流れてるトラフィック量を表示する

というのをつくってみた。Internet Week 2009のこのBOFで紹介されてたのの劣化版ともいう。

#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# snmpgetをつかって定期的にカウンタの値をとってきて表示する
# ターゲットはCSVファイルで指定
#
import sys
import csv
import re
import optparse
import subprocess
import select

# コマンドラインオプションを解析する
def parse_options():
	parser = optparse.OptionParser()
	parser.add_option("-c", "--snmpget", dest="snmpget", default="/usr/bin/snmpget",
                    help="snmpget path", type="string", action="store")
	parser.add_option("-f", "--file", dest="filename", default="iflist.csv",
                    help="config file", type="string", action="store")
	parser.add_option("-i", "--interval", dest="interval", default=10,
	                  help="snmpget interval(sec)", type="int", action="store")
	(options, args) = parser.parse_args()
	csvfile  = options.filename
	interval = options.interval
	snmpget  = options.snmpget
	return (csvfile, interval, snmpget)

# 設定ファイル=CSVファイルを読む
# 設定ファイルのフォーマットは下記のとおり
# ターゲットのIPアドレス,コミュニティ名,OID,表示名,SNMPバージョン
def read_config(csvfile):
	iflist = []
	reader = csv.reader(file(csvfile, "rb"))
	for row in reader:
		(ipaddress, community, oid, description, version) = row
		iflist.append({
			'ipaddress'   : ipaddress,
			'community'   : community,
			'oid'         : oid,
			'description' : description,
			'version'     : version,
			'oldvalue'    : 0 # snmpget's old value
		})
	return iflist

# タイムアウトつきreadline
# http://funini.com/kei/py/select.shtml
def readline_timeout(fd, timeout):
	(r, w, e) = select.select([fd], [], [], timeout)
	if len(r) > 0:
		return r[0].readline()
	else:
		return None

# snmpgetコマンドで定期的にとってきて表示
def report(snmpget, iflist, interval):
	endflag = False
	while not endflag:
		for line in iflist:
			# subprocess.Popen()を使ってsnmpgetを実行する
			snmp = subprocess.Popen(["%s -v %s -c %s %s %s"
				% (snmpget, line['version'], line['community'],
					line['ipaddress'], line['oid'])],
				shell=True, stdout=subprocess.PIPE)
			# snmpgetコマンドの出力結果を取得する
			l = snmp.stdout.readline()
			m = re.match('^\S+ = (\S+): (\d+)$', l)
			(valuetype, valuestring) = m.groups()
			snmp.wait()
			# 取得したカウンタの種類によって、補正に使う値を決める
			# countermax : 64bit : 2^64 32bit : 2^32
			if valuetype == 'Counter32':
				countermax = 2 ** 32
			elif valuetype == 'Counter64':
				countermax = 2 ** 64
			else:
				# 今のところCounter32とCounter64のみ対応とするので、
				# それ以外は落ちてもらいます
				assert False
			value = int(valuestring)
			# カウンタがオーバーフローしてたら補正
			if value < line['oldvalue']:
				v = value + countermax
			else:
				v = value
			traffic = (v - line['oldvalue']) * 8.0 / interval
			# コマンド実行直後だったら表示しない
			if line['oldvalue'] == 0:
				line['oldvalue'] = value
				continue
			else: 
				line['oldvalue'] = value
			# トラフィック量に応じて単位を調整して表示
			if traffic > 1000.0 * 1000.0 * 1000.0 :
				# over 1Gbps
				print "%s : %f Gbps" % (line['description'], traffic / 1000.0 / 1000.0 / 1000.0)
			elif traffic > 1000.0 * 1000.0 :
				# over 1Mbps
				print "%s : %f Mbps" % (line['description'], traffic / 1000.0 / 1000.0)
			elif traffic > 1000.0 :
				# over 1kbps
				print "%s : %f kbps" % (line['description'], traffic / 1000.0 )
			else:
				# under 1kbps
				print "%s : %f bps" % (line['description'], traffic)
		# タイムアウトつきreadlineをつかって、
		# なにか入力されたらおわりとして、
		# なにも入力されなかったらループする
		s = readline_timeout(sys.stdin, interval)
		if s != None:
			endflag = True

# メイン
def main():
	(csvfile, interval, snmpget) = parse_options()
	iflist = read_config(csvfile)
	report(snmpget, iflist, interval)

if __name__ == '__main__':
  main()

ターゲットの情報はCSVファイルに書く。デフォルトは10秒平均。