2015年10月31日
オープンソースの SNMP エージェント・シミュレータ - snmposter と「俺、 fork したよ」って話
僕がブログを始めたのは、まだその頃珍しかった Java 言語で記述されたオープンソースの SNMP(Simple Network Management Protocol)ライブラリ SNMP4J(http://www.snmp4j.org)の詳しい説明をエントリすれば月間 100 万 PV ぐらい稼いで広告収入でアーリー・リタイアできるかもって考えたのがきっかけでした。 でも、結局・・・全然・・・そんなことにはならなくて、今もまだ(まあまあ)真面目に働いてます。 今日は SNMP マネージャーの開発や評価で必要になる SNMP エージェント・シュミレーターの1つ、snmposter を紹介をします。
snmposter エージェント・シミュレータの基本的な使い方
SNMP では監視される側のネットワーク機器(ルーター、スイッチ、サーバー)をエージェント、エージェントからその機器の状態(状態は MIB/Management Information Base という標準化されたデータ構造で管理されています)を収集して管理・監視する側をマネージャっていいます。 snmposter は Python で記述された SNMP エージェント・シミュレータで、ルーターやスイッチそのもの(デバイス)が無くても、それらをソフトウェアでシミュレーションしてくれるツールです。
snmposter は監視ソリューションを開発・販売する Zenoss, Inc. に所属する Chet Luther さん(https://github.com/cluther/snmposter)によって開発されていて、Ubuntu(僕は Ubuntu 14.04 Server 上で動かしています)であれば次の手順でインストールできます。
エージェントが SNMPv2c をサポートしているなら SNMP GETBULK リクエストを使った方が効率的です(ただし、エージェントのピーク負荷は高くなるかもしれません)。
walk した結果をリダイレクトしたファイルの中身は OID、(プリミティブな)データ・タイプ、値という形式になっていて、snmposter はこのデータを読み込み、シミュレーションしてくれるわけです。
試しに csv ファイルで指定したアドレスに対して snmpget してみて下さい(SNMP マネージャと snmposter を異なるホスト上で動かす場合、マネージャーから snmposter ホストに対してエージェント・アドレスへのルートを切る必要があります)。
きっと僕と同じように SNMP GET レスポンスを受け取ることができるはずです。
あと、snmposter は daemonize されていて停止するにはいちいち PID を調べて kill -kill する必要があります。 これは面倒なので起動・停止シェルを用意しておくのが良いと思います。
SNMP シミュレータを使う理由の多分殆どは SNMP マネージャーを評価、開発するためです。 で、僕もその目的で snmposter にたどり着き、概ね満足したのですが 1 つだけ不満がありました。 snmposter のような非商用の・簡単な SNMP シミュレータではエージェントのオブジェクト値を動的に変えることができないんです。
snmposter の基本的な使い方に変わりありませんが、-w オプションで Web ポートを指定できるようになっています(指定しない場合はデフォルトで 8888 になります)。
ほら、エージェントの値も変わりました。
$ sudo apt-get install gcc python-dev python-pip
$ sudo pip install snmposter
シミュレーションする MIB オブジェクトのデータは net-snmp に含まれる snmpwalk, snmpbulkwalk を使って集めることができます(この学習にデスクトップ環境を使わない点も僕が snmposter を気に入った理由の1つです)。
例えば、シミュレーションしたいルーターのアドレスが 192.168.0.1 だったとしたら、次のようにして SNMP エージェントの MIB オブジェクト・ツリーをテッペン(.1)から全て学習できます。
# SNMPv1
$ snmpwalk -v1 -c public -ObenU 192.168.0.1 .1 > router.snmpwalk
# SNMPv2c
$ snmpbulkwalk -v 2c -c public -ObenU 192.168.0.1 .1 > router.snmpwalk
エージェントが SNMPv2c をサポートしているなら SNMP GETBULK リクエストを使った方が効率的です(ただし、エージェントのピーク負荷は高くなるかもしれません)。
walk した結果をリダイレクトしたファイルの中身は OID、(プリミティブな)データ・タイプ、値という形式になっていて、snmposter はこのデータを読み込み、シミュレーションしてくれるわけです。
$ head router.snmpwalk
.1.3.6.1.2.1.1.1.0 = STRING: "RTX1200 Rev.10.01.59 (Tue Aug 19 19:26:02 2014)"
.1.3.6.1.2.1.1.2.0 = OID: .1.3.6.1.4.1.1182.1.37
.1.3.6.1.2.1.1.3.0 = Timeticks: (880537345) 101 days, 21:56:13.45
.1.3.6.1.2.1.1.4.0 = ""
.1.3.6.1.2.1.1.5.0 = ""
.1.3.6.1.2.1.1.6.0 = ""
.1.3.6.1.2.1.1.7.0 = INTEGER: 12
.1.3.6.1.2.1.2.1.0 = INTEGER: 5746
.1.3.6.1.2.1.2.2.1.1.1 = INTEGER: 1
.1.3.6.1.2.1.2.2.1.1.2 = INTEGER: 2
1つ注意して欲しいのは net-snmp のバージョンによってはオブジェクト値だけでデータ・タイプを出力しないことがあるって点です。
先の例では OID: .1.3.6.1.2.1.1.4.0(iso.identified-organization.dod.internet.mgmt.mib-2.system.sysContact.0)の値が空文字なので、そのデータ・タイプが出力されていません。 このようなオブジェクトは snmposter では無視されてしまいシミュレーションできないのです。
これもシミュレーションしたいデータであればファイルを編集してデータ・タイプ(この例でいうと STRING:)を追記する必要があります(MIB ファイルでは sysContact のデータ型は DisplayString かもしれません。 ただ、ここでは原始的な型を定義するので STRING: になります)。
先の例では OID: .1.3.6.1.2.1.1.4.0(iso.identified-organization.dod.internet.mgmt.mib-2.system.sysContact.0)の値が空文字なので、そのデータ・タイプが出力されていません。 このようなオブジェクトは snmposter では無視されてしまいシミュレーションできないのです。
これもシミュレーションしたいデータであればファイルを編集してデータ・タイプ(この例でいうと STRING:)を追記する必要があります(MIB ファイルでは sysContact のデータ型は DisplayString かもしれません。 ただ、ここでは原始的な型を定義するので STRING: になります)。
ちなみにこのファイルの行数、つまり MIB オブジェクト数が 20 万を超えるエンタープライズ・ルーターのシミュレーションも試しましたが僕の手元ではとてもうまく動きました(このとき snmposter は 300 MB ほどメモリを使います)!
シミュレーション(学習データ)・ファイルを用意したら csv ファイルにそのパスと仮想エージェントのアドレスを指定しましょう(複数可)。
/path/to/agents/router.snmpwalk,192.0.0.1
...
このファイルを agents.csv とすれば次のコマンドでシミュレーションを始めることができます。
$ sudo snmposter -f /path/to/agents.csv
試しに csv ファイルで指定したアドレスに対して snmpget してみて下さい(SNMP マネージャと snmposter を異なるホスト上で動かす場合、マネージャーから snmposter ホストに対してエージェント・アドレスへのルートを切る必要があります)。
きっと僕と同じように SNMP GET レスポンスを受け取ることができるはずです。
# snmpget system.sysDescr.0 (manager-host $ sudo route add -host 192.0.0.1 gw snmposter-host)
$ snmpget -v 2c -c public 192.0.0.1 .1.3.6.1.2.1.1.1.0
iso.3.6.1.2.1.1.1.0 = STRING: "RTX1200 Rev.10.01.59 (Tue Aug 19 19:26:02 2014)"
あと、snmposter は daemonize されていて停止するにはいちいち PID を調べて kill -kill する必要があります。 これは面倒なので起動・停止シェルを用意しておくのが良いと思います。
#!/bin/sh
CURRENT_DIR=$(cd $(dirname $0) && pwd)
AGENT_CSV=/path/to/agent.csv
is_exist() {
ps auxw | grep snmposter | grep -v grep > /dev/null 2>&1
return $?
}
case $1 in
"start")
if is_exist;
then
echo "failed. snmposter already started."
ps auxw | grep snmposter | grep -v grep
fi
echo "starting snmposter..."
snmposter -f $AGENT_CSV
if [ $? -eq 0 ];
then
echo "success."
SNMPOSTER_PID=` ps auxw | grep snmposter | grep -v grep | sed 's/ */,/g' | cut -d ',' -f2`
echo $SNMPOSTER_PID > $CURRENT_DIR/.snmposter-pid
else
echo "failed."
fi
;;
"stop")
echo "stopping snmposter..."
kill -kill `cat $CURRENT_DIR/.snmposter-pid`
if is_exist;
then
echo "failed"
else
rm -rf $CURRENT_DIR/.snmposter-pid
fi
;;
*)
echo "args: start or stop"
;;
esac
ダイナミックにオブジェクトの値を変えたい。 だから snmposter を fork して WebAPI を追加した。
SNMP シミュレータを使う理由の多分殆どは SNMP マネージャーを評価、開発するためです。 で、僕もその目的で snmposter にたどり着き、概ね満足したのですが 1 つだけ不満がありました。 snmposter のような非商用の・簡単な SNMP シミュレータではエージェントのオブジェクト値を動的に変えることができないんです。
例えば僕が SNMP マネージャー機能をもったネットワーク管理システムを開発していて、エージェントの状態が急激に変化・悪化したことを検知、復旧ワークフローを自動実行する機能を開発・テストしたいとします。 でも snmposter でシミュレートする値は常に静的で、状態に変化を起こすことはできません(snmpwalk ファイルを編集して snmposter を再起動するって方法はありますがスマートじゃないですし、テストの自動化と相性が悪いです)。
そこで僕は Chet Luther さんの snmposter をフォーク(https://github.com/netbuffalo/snmposter)して SNMP エージェントの状態(インスタンス値)を動的に変更できる WebAPI を付け加えることにしました。
pip は使えないのでインストールは次の手順になります。
pip は使えないのでインストールは次の手順になります。
# install dependencies.
$ sudo apt-get install gcc python-dev python-setuptools python-tornado git-core
# install TwistedSNMP.
$ wget http://downloads.sourceforge.net/project/twistedsnmp/twistedsnmp/0.3.13/TwistedSNMP-0.3.13.tar.gz
$ tar -xzf TwistedSNMP-0.3.13.tar.gz
$ cd TwistedSNMP-0.3.13
$ sudo python setup.py install
$ cd ..
# install pysnmp.
$ wget http://downloads.sourceforge.net/project/twistedsnmp/pysnmp-se/3.5.2/pysnmp-se-3.5.2.tar.gz
$ tar -xzf pysnmp-se-3.5.2.tar.gz
$ cd pysnmp-se-3.5.2
$ sudo python setup.py install
$ cd ..
# install my (netbuffalo) snmposter.
$ git clone https://github.com/netbuffalo/snmposter.git
$ cd snmposter
$ sudo python setup.py install
$ cd ..
snmposter の基本的な使い方に変わりありませんが、-w オプションで Web ポートを指定できるようになっています(指定しない場合はデフォルトで 8888 になります)。
$ sudo snmposter -f agents.csv -w 8888
僕の snmposter は、このポートへエージェントのアドレスと更新したいオブジェクトの配列を定義した json データを POST(パスは /mib/oper/update です)すると、動的にインスタンスの値を変えられるようになっています。
例えば、インターフェース・インデックス 4 の ifInOctets と ifOutOctets を更新するならこんな json になります。
$ cat /path/to/traffic.json
{
"192.0.0.1": [
{ "oid":".1.3.6.1.2.1.2.2.1.10.4" , "type":"Counter32", "value":"55937184" },
{ "oid":".1.3.6.1.2.1.2.2.1.16.4" , "type":"Counter32", "value":"252553667" }
]
}
これを、例えば curl を使って snmposter サーバー(ここでは 192.168.33.100)へ POST すると、
$ curl -v -H "Content-type: application/json" -X POST --data @/path/to/traffic.json \
http://192.168.33.100:8888/mib/oper/update
* Hostname was NOT found in DNS cache
* Trying 192.168.33.100...
* Connected to 192.168.33.100 (192.168.33.100) port 8888 (#0)
> POST /mib/oper/update HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.33.100:8888
> Accept: */*
> Content-type: application/json
> Content-Length: 189
>
* upload completely sent off: 189 out of 189 bytes
< HTTP/1.1 200 OK
< Date: Sat, 31 Oct 2015 09:47:46 GMT
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
* Server TornadoServer/3.1.1 is not blacklisted
< Server: TornadoServer/3.1.1
<
* Connection #0 to host 192.168.33.100 left intact
ほら、エージェントの値も変わりました。
$ snmpbulkwalk -v 2c -ObenU -c public 192.0.0.1 IF-MIB::ifInOctets
.1.3.6.1.2.1.2.2.1.10.1 = Counter32: 1913180477
.1.3.6.1.2.1.2.2.1.10.2 = Counter32: 455937184
.1.3.6.1.2.1.2.2.1.10.3 = Counter32: 0
.1.3.6.1.2.1.2.2.1.10.4 = Counter32: 55937184 # ifInOctets.4
.1.3.6.1.2.1.2.2.1.10.5 = Counter32: 0
...
これで動的・より複雑なテストができますね!
じゃあ、また今度。
ダグラス・R. マウロ ケビン・J. シュミット
オライリー・ジャパン
売り上げランキング: 230,732
オライリー・ジャパン
売り上げランキング: 230,732
実践SNMP教科書―ネットワーク管理ツールの開発と活用 (IT TEXT)
posted with amazlet at 15.10.31
山居 正幸
CQ出版
売り上げランキング: 739,503
CQ出版
売り上げランキング: 739,503
この記事へのトラックバックURL
この記事へのコメント
「新宿の坂本一生」と申します。 素晴らしい記事 なので、また見たいと思います。 更新楽しみにしてます。
Posted by 坂本一生 at 2017年08月12日 16:47