簡単SNMP(監視カスタマイズ編)
SNMPで誤解してた部分を勉強し直したついでに纏めてみた。
前回の続き
net-snmp の監視項目をカスタマイズする方法
主に3通りの方法がある。
/etc/snmp/snmpd.conf exec コマンド
net-snmpに任意のコマンドを登録する。
- /etc/snmp/snmpd.conf
# exec コマンド名 コマンド [引数..] exec cmd1 /bin/echo FOO exec cmd2 /bin/echo BAR exec cmd3 /bin/cat /etc/issue
net-snmp拡張 [enterprises]-[ucdavis]-[extTable]以下に結果が配置される。
$ snmpwalk -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.2021.8.1 UCD-SNMP-MIB::extIndex.1 = INTEGER: 1 UCD-SNMP-MIB::extIndex.2 = INTEGER: 2 UCD-SNMP-MIB::extIndex.3 = INTEGER: 3 UCD-SNMP-MIB::extNames.1 = STRING: cmd1 UCD-SNMP-MIB::extNames.2 = STRING: cmd2 UCD-SNMP-MIB::extNames.3 = STRING: cmd3 UCD-SNMP-MIB::extCommand.1 = STRING: /bin/echo UCD-SNMP-MIB::extCommand.2 = STRING: /bin/echo UCD-SNMP-MIB::extCommand.3 = STRING: /bin/cat UCD-SNMP-MIB::extResult.1 = INTEGER: 0 UCD-SNMP-MIB::extResult.2 = INTEGER: 0 UCD-SNMP-MIB::extResult.3 = INTEGER: 0 UCD-SNMP-MIB::extOutput.1 = STRING: FOO UCD-SNMP-MIB::extOutput.2 = STRING: BAR UCD-SNMP-MIB::extOutput.3 = STRING: CentOS release 6.2 (Final) UCD-SNMP-MIB::extErrFix.1 = INTEGER: noError(0) UCD-SNMP-MIB::extErrFix.2 = INTEGER: noError(0) UCD-SNMP-MIB::extErrFix.3 = INTEGER: noError(0) UCD-SNMP-MIB::extErrFixCmd.1 = STRING: UCD-SNMP-MIB::extErrFixCmd.2 = STRING: UCD-SNMP-MIB::extErrFixCmd.3 = STRING:
/etc/snmp/snmpd.conf extend コマンド
基本的には上記execと変わらない。OIDが固定できるので重宝する。
- /etc/snmp/snmpd.conf
extend ext1 /bin/cat /etc/hosts
この様に名前="ext1" がOIDとなるので参照し易い。
$ snmpwalk -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.8072.1.3.2 NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 1 NET-SNMP-EXTEND-MIB::nsExtendCommand."ext1" = STRING: /bin/cat NET-SNMP-EXTEND-MIB::nsExtendArgs."ext1" = STRING: /etc/hosts NET-SNMP-EXTEND-MIB::nsExtendInput."ext1" = STRING: NET-SNMP-EXTEND-MIB::nsExtendCacheTime."ext1" = INTEGER: 5 NET-SNMP-EXTEND-MIB::nsExtendExecType."ext1" = INTEGER: exec(1) NET-SNMP-EXTEND-MIB::nsExtendRunType."ext1" = INTEGER: run-on-read(1) NET-SNMP-EXTEND-MIB::nsExtendStorage."ext1" = INTEGER: permanent(4) NET-SNMP-EXTEND-MIB::nsExtendStatus."ext1" = INTEGER: active(1) NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."ext1" = STRING: 127.0.0.1 localhost localhost.localdomain NET-SNMP-EXTEND-MIB::nsExtendOutputFull."ext1" = STRING: 127.0.0.1 localhost localhost.localdomain ::1 localhost localhost.localdomain NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."ext1" = INTEGER: 2 NET-SNMP-EXTEND-MIB::nsExtendResult."ext1" = INTEGER: 0 NET-SNMP-EXTEND-MIB::nsExtendOutLine."ext1".1 = STRING: 127.0.0.1 localhost localhost.localdomain NET-SNMP-EXTEND-MIB::nsExtendOutLine."ext1".2 = STRING: ::1 localhost localhost.localdomain
Sub agent
自前のモジュール(プログラム)を組み込む。
手法は大きく分けて、以下の3つ
- 静的リンク
- net-snmp再ビルドする方法。
運用負担が大きすぎて使い難い。 - 動的リンク
- snmpd.confにdlmodを指定し(??.so)とリンクする。
snmpdの再起動でプログラムの変更が反映できる
しかしバグったプログラムを組み込むとsnmpd諸共落ちるのでイマイチ。 - プロセス間通信
- Sub-Agentを別プロセスとして立て、ソケット通信する。
一番運用し易いと思われる。Sub-Agent自体が落ちた場合はその範囲のMIBが読めなくなるのでsub-agentプロセスの監視も楽。
Sub agent 作成
MIBを作る
/usr/share/snmp/mibs/MY-MIB.txt
MY-MIB DEFINITIONS ::= BEGIN IMPORTS OBJECT-TYPE, MODULE-IDENTITY,Integer32, enterprises FROM SNMPv2-SMI; mymib MODULE-IDENTITY LAST-UPDATED "1303120000Z" ORGANIZATION "Crumb" CONTACT-INFO "" DESCRIPTION "" ::= { enterprises 9999 } myobj OBJECT IDENTIFIER ::= { mymib 1 } myint1 OBJECT-TYPE SYNTAX Integer32 (0..2147483647) MAX-ACCESS read-write STATUS current DESCRIPTION "Custom Integer 1" ::= { myobj 1 } mystring2 OBJECT-TYPE SYNTAX OCTET STRING (SIZE(0..1024)) MAX-ACCESS read-write STATUS current DESCRIPTION "Custom String 2" ::= { myobj 2 } END
mib2c
- net-snmpのヘッダをインストール
# yum install net-snmp-deval
mib2cというツールを使い、スケルトンを作ります。
- mib2cインストール
# yum install net-snmp-perl
- mib2c実行
$ mkdir /tmp/mymib $ MIBS="+/usr/share/snmp/mibs/MY-MIB.txt" mib2c mymib プロンプトでは 2) Net-SNMP style code と 1) If you're writing code for some generic scalars (by hand use: "mib2c -c mib2c.scalar.conf mymib") を選択
コーディング
#include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> #include <net-snmp/agent/net-snmp-agent-includes.h> #include "mymib.h" long myint1 = 0; // 追記:initial = 1 char *mystring2 = 0; // 追記:initial = NULL void init_mymib(void) { const oid myint1_oid[] = { 1,3,6,1,4,1,9999,1,1 }; const oid mystring2_oid[] = { 1,3,6,1,4,1,9999,1,2 }; DEBUGMSGTL(("mymib", "Initializing\n")); netsnmp_register_scalar( netsnmp_create_handler_registration("myint1", handle_myint1, myint1_oid, OID_LENGTH(myint1_oid), HANDLER_CAN_RWRITE )); netsnmp_register_scalar( netsnmp_create_handler_registration("mystring2", handle_mystring2, mystring2_oid, OID_LENGTH(mystring2_oid), HANDLER_CAN_RWRITE )); mystring2=(char*)malloc(1); mystring2[0]=0; // "" } int handle_myint1(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int ret; switch(reqinfo->mode) { case MODE_GET: snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) &myint1, // myint1アドレスを指定 sizeof(myint1) ); // myint1サイズを指定 break; case MODE_SET_RESERVE1: ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER); if ( ret != SNMP_ERR_NOERROR ) { netsnmp_set_request_error(reqinfo, requests, ret ); } break; case MODE_SET_RESERVE2: break; case MODE_SET_FREE: break; case MODE_SET_ACTION: break; case MODE_SET_COMMIT: myint1 = *requests->requestvb->val.integer; // 指定された値で更新 break; case MODE_SET_UNDO: break; default: snmp_log(LOG_ERR, "unknown mode (%d) in handle_myint1\n", reqinfo->mode ); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; } int handle_mystring2(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int ret; switch(reqinfo->mode) { case MODE_GET: snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, (u_char *)mystring2, // mystring2アドレスを指定 strlen(mystring2+1)); // mystring2サイズを指定 break; case MODE_SET_RESERVE1: ret = netsnmp_check_vb_type(requests->requestvb, ASN_OCTET_STR); if ( ret != SNMP_ERR_NOERROR ) { netsnmp_set_request_error(reqinfo, requests, ret ); } break; case MODE_SET_RESERVE2: break; case MODE_SET_FREE: break; case MODE_SET_ACTION: break; case MODE_SET_COMMIT: free(mystring2); // 現在の値をクリア mystring2 = strdup((char*)requests->requestvb->val.string); // 指定された値で更新 break; case MODE_SET_UNDO: break; default: snmp_log(LOG_ERR, "unknown mode (%d) in handle_mystring2\n", reqinfo->mode ); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; }
本来はハンドラが呼ばれる順も気にしながら書くのだが、ココでは最小限の
- MODE_GET
- MODE_SET_COMMIT
しか扱わない。
GETで値を返して、SET_COMMITで指定された値を保存するだけ。
agentX設定
- /etc/snmp/snmpd.conf
- 次の行を指定。
master agentx
snmpd を再起動するとソケット(/var/agentx/master)が出来る。
このソケットがagentX sub agent のI/Fとなる。
/var/agentx のパーミッションでrootしかアクセスできないソケットになっている。
Sub agent 起動
# ./mymib NET-SNMP version 5.5 AgentX subagent connected
試しにsnmpwalkしてみると
snmpwalk -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.9999 MY-MIB::myint1.0 = INTEGER: 0 MY-MIB::mystring2.0 = ""
ちゃんとコーディングで指定した初期値が取れました。
値更新
監視するだけならばsnmpgetされた時にsub agentで何かを監視して値を更新すれば良い。
しかしsnmpsetを利用するとある程度の運用をリモートで行う事もできる。
- /etc/snmp/snmpd.conf
- 書き込み用ビューを設定
view rwview included 1.3.6.1.4.1.9999
snmpd再起動
- 値更新
- sub agentの幾つかのハンドラが動き、エラーが無ければ最終的にSET_COMMITハンドラが動く。
例えば『INTEGER:99が指定された際にはapacheの再起動を行う。』ようなコードを書いておけば
リモートからのsnmpsetでapacheの再起動の運用が出来る。
$ snmpset -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.9999.1.1.0 i 1 MY-MIB::myint1.0 = INTEGER: 1 $ snmpset -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.9999.1.2.0 s aaaaaaa MY-MIB::mystring2.0 = STRING: "aaaaaaa"
更新後の値を確認
$ snmpwalk -v 3 -lauthNoPriv -u myuser -a MD5 -A mypasswd 127.0.0.1 1.3.6.1.4.1.9999 MY-MIB::myint1.0 = INTEGER: 1 MY-MIB::mystring2.0 = STRING: "aaaaaa"
まとめ
SNMPはMIBツリーの構造が複雑だが、逆に高度に構造化されているので、解ってしまえばシンプル。
ツリー化されてるので拡張性に優れており高度な自動化にはうってつけだろう。
最近は運用面はChef/Capistranoという選択肢もあるが、、監視と運用を一体化できるので、僕はSNMPの方が好みかな。
SNMPモジュールをデプロイする為にChef/Capistranoを使う?