2015年10月31日

オープンソースの SNMP エージェント・シミュレータ - snmposter と「俺、 fork したよ」って話

僕がブログを始めたのは、まだその頃珍しかった Java 言語で記述されたオープンソースの SNMP(Simple Network Management Protocol)ライブラリ SNMP4J(http://www.snmp4j.org)の詳しい説明をエントリすれば月間 100 万 PV ぐらい稼いで広告収入でアーリー・リタイアできるかもって考えたのがきっかけでした。 でも、結局・・・全然・・・そんなことにはならなくて、今もまだ(まあまあ)真面目に働いてます。 今日は SNMP マネージャーの開発や評価で必要になる SNMP エージェント・シュミレーターの1つ、snmposter を紹介をします。

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 上で動かしています)であれば次の手順でインストールできます。

$ 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: になります)。

ちなみにこのファイルの行数、つまり 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 を再起動するって方法はありますがスマートじゃないですし、テストの自動化と相性が悪いです)。

Zabbix traffice graph 1


そこで僕は Chet Luther さんの snmposter をフォーク(https://github.com/netbuffalo/snmposter)して SNMP エージェントの状態(インスタンス値)を動的に変更できる WebAPI を付け加えることにしました。

snmposter network


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
...


これで動的・より複雑なテストができますね!

Zabbix traffice graph 2


じゃあ、また今度。

入門SNMP
入門SNMP
posted with amazlet at 15.10.31
ダグラス・R. マウロ ケビン・J. シュミット
オライリー・ジャパン
売り上げランキング: 230,732

実践SNMP教科書―ネットワーク管理ツールの開発と活用 (IT TEXT)
山居 正幸
CQ出版
売り上げランキング: 739,503

Posted by netbuffalo at 20:41│Comments(1)TrackBack(0)実践SNMP+Java | ネットワーク


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/5133720
この記事へのコメント
「新宿の坂本一生」と申します。 素晴らしい記事 なので、また見たいと思います。 更新楽しみにしてます。
Posted by 坂本一生 at 2017年08月12日 16:47

コメントする

名前
URL
 
  絵文字