19.6.5. Net-SNMP の拡張

Net-SNMP Agent は、raw システムメトリックに加えてアプリケーションメトリックを提供するために拡張することができます。これにより、パフォーマンス問題のトラブルシューティングだけでなく容量計画も行うことができます。例えば、試験中に電子メールシステムの 5 分の負荷平均が 15 であったことを把握しておくことは役に立つかもしれませんが、毎秒 80,000 メッセージの処理中に電子メールシステムの負荷平均が 15 であることを知っておく方がはるかに役立ちます。アプリケーションメトリックがシステムメトリックと同じインターフェースで使用可能な場合、システムパフォーマンスの様々な負荷状況の影響も視覚化することができます (例えば 10,000 メッセージが追加されると、負荷平均は 100,000 まで直線的に増加します)。
Red Hat Enterprise Linux に同梱されているアプリケーションの多くは、Net-SNMP Agent を拡張して、SNMP によるアプリケーションメトリックを提供します。カスタムアプリケーション用にエージェントを拡張する方法もいくつかあります。本項では、シェルスクリプトと Perl プラグインを使ったエージェントの拡張方法について説明します。本項では、net-snmp-utils 及び net-snmp-perl パッケージがインストールされ、「認証の設定」 で説明のとおりユーザーは SNMP ツリーへのアクセスが許可されていることを前提としています。

19.6.5.1. シェルスクリプトによる Net-SNMP の拡張

Net-SNMP Agent は、任意のシェルスクリプトをクエリするために使用可能な拡張 MIB (NET-SNMP-EXTEND-MIB) を提供します。実行するシェルスクリプトを指定するには、/etc/snmp/snmpd.conf ファイルの extend 指示文を使用します。定義されると、Agent は SNMP により終了コードとコマンドの出力を提供します。以下の例は、プロセステーブルの httpd プロセスの数を決定するスクリプトを使ってこの仕組みを説明しています。

注記

Net-SNMP Agent には、proc 指示文によりプロセステーブルを確認する組み込みメカニズムも備わっています。詳細については snmpd.conf(5) の man ページを参照して下さい。
以下のシェルスクリプトの終了コードは、任意の時点におけるシステム上で実行中の httpd プロセスの数です:
#!/bin/sh

NUMPIDS=`pgrep httpd | wc -l`

exit $NUMPIDS
SNMP でこのスクリプトを利用可能にするには、システムパス上にある場所にスクリプトをコピー後、実行ビットを設定して、extend 指示文を /etc/snmp/snmpd.conf ファイルに追加します。extend 指示文の形式は、以下のとおりです:
extend name prog args
name は拡張するための識別文字列、prog は実行するプログラム、args はプログラムに渡す引数です。例として、上記のシェルスクリプトが /usr/local/bin/check_apache.sh にコピーされた場合、以下の指示文は SNMP ツリーにスクリプトを追加します:
extend httpd_pids /bin/sh /usr/local/bin/check_apache.sh
スクリプトは NET-SNMP-EXTEND-MIB::nsExtendObjects でクエリできます:
~]$ snmpwalk localhost NET-SNMP-EXTEND-MIB::nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendCommand."httpd_pids" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."httpd_pids" = STRING: /usr/local/bin/check_apache.sh
NET-SNMP-EXTEND-MIB::nsExtendInput."httpd_pids" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."httpd_pids" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."httpd_pids" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."httpd_pids" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."httpd_pids" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStatus."httpd_pids" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."httpd_pids" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."httpd_pids" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."httpd_pids" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendResult."httpd_pids" = INTEGER: 8
NET-SNMP-EXTEND-MIB::nsExtendOutLine."httpd_pids".1 = STRING:
注意する点は、終了コード (この例では 8) は INTEGER (整数) タイプとして、出力は STRING (文字列) タイプとして、示されていることです。複数のメトリックを整数として表示するには、extend 指示文を使用してスクリプトに異なる引数を指定します。例えば、以下のシェルスクリプトを使用すると、任意の文字列に一致するプロセスの数を見つけ出すことができ、プロセスの数を示すテキスト文字列も出力します:
#!/bin/sh

PATTERN=$1
NUMPIDS=`pgrep $PATTERN | wc -l`

echo "There are $NUMPIDS $PATTERN processes."
exit $NUMPIDS
次の /etc/snmp/snmpd.conf 指示文は、上記のスクリプトが /usr/local/bin/check_proc.sh にコピーされる時に httpd PID の数と併せて snmpd PID の数を与えます:
extend httpd_pids /bin/sh /usr/local/bin/check_proc.sh httpd
extend snmpd_pids /bin/sh /usr/local/bin/check_proc.sh snmpd
以下の例は、nsExtendObjects OID の snmpwalk の出力を示しています:
~]$ snmpwalk localhost NET-SNMP-EXTEND-MIB::nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 2
NET-SNMP-EXTEND-MIB::nsExtendCommand."httpd_pids" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendCommand."snmpd_pids" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."httpd_pids" = STRING: /usr/local/bin/check_proc.sh httpd
NET-SNMP-EXTEND-MIB::nsExtendArgs."snmpd_pids" = STRING: /usr/local/bin/check_proc.sh snmpd
NET-SNMP-EXTEND-MIB::nsExtendInput."httpd_pids" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendInput."snmpd_pids" = STRING:
...
NET-SNMP-EXTEND-MIB::nsExtendResult."httpd_pids" = INTEGER: 8
NET-SNMP-EXTEND-MIB::nsExtendResult."snmpd_pids" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendOutLine."httpd_pids".1 = STRING: There are 8 httpd processes.
NET-SNMP-EXTEND-MIB::nsExtendOutLine."snmpd_pids".1 = STRING: There are 1 snmpd processes.

警告

整数の終了コードは 0 から 255 の範囲に制限されています。256 を超える可能性がある値については、スクリプトの標準出力 (文字列として入力されるもの) を使用するか、エージェントを拡張するという別の方法を実行して下さい。
この最後の例では、システムの空きメモリと httpd プロセスの数のクエリを示しています。このクエリは、パフォーマンステスト中にメモリ負担に与えるプロセス数の影響を知るために使用することができます:
~]$ snmpget localhost \
    'NET-SNMP-EXTEND-MIB::nsExtendResult."httpd_pids"' \
    UCD-SNMP-MIB::memAvailReal.0
NET-SNMP-EXTEND-MIB::nsExtendResult."httpd_pids" = INTEGER: 8
UCD-SNMP-MIB::memAvailReal.0 = INTEGER: 799664 kB

19.6.5.2. Perl による Net-SNMP の拡張

extend 指示文を使用してシェルスクリプトを実行することは、SNMP によるカスタムのアプリケーションメトリックを表示するにはかなり限られた方法です。Net-SNMP Agent は、カスタムオブジェクトを表示するための埋め込み Perl インターフェースも提供します。net-snmp-perl パッケージには、Red Hat Enterprise Linux に埋め込み Perl プラグインを書き込むために使用される NetSNMP::agent Perl モジュールがあります。
NetSNMP::agent Perl モジュールは、エージェントの OID ツリーの一部に対する要求を処理するために使用される agent オブジェクトを提供します。agent オブジェクトのコンストラクターには、エージェントを snmpd のサブエージェントまたはスタンドアロンエージェントとして実行するためのオプションがあります。埋め込みエージェントを作成するために必要な引数はありません:
use NetSNMP::agent (':all');

my $agent = new NetSNMP::agent();
agent オブジェクトには、コールバック関数を特定の OID に登録するために使用される register メソッドがあります。register 関数は、名前、OID、コールバック関数へのポインターを取ります。以下の例は、hello_handler と呼ばれるコールバック関数を OID .1.3.6.1.4.1.8072.9999.9999 で要求を処理する SNMP Agent に登録しています:
$agent->register("hello_world", ".1.3.6.1.4.1.8072.9999.9999",
                 \&hello_handler);

注記

通常、OID .1.3.6.1.4.1.8072.9999.9999 (NET-SNMP-MIB::netSnmpPlaypen) は表示目的のみに使用されます。お客様の組織に root OID がない場合は、ISO Name Registration Authority (米国では ANSI) にご連絡いただくと取得できます。
ハンドラー関数は、HANDLERREGISTRATION_INFOREQUEST_INFOREQUESTS の 4 つのパラメーターで呼び出されます。REQUESTS パラメーターには、現在の呼び出しの要求一覧が含まれており、反復されデータが追加されるはずです。一覧の request オブジェクトには get 及び set メソッドがあるため、要求の OIDvalue を操作することができます。例として、以下の呼び出しは hello world という文字列に要求オブジェクトの値を設定します:
$request->setValue(ASN_OCTET_STR, "hello world");
ハンドラー関数は、GET 要求と GETNEXT 要求という 2 種類の SNMP 要求に応答できます。要求のタイプは、ハンドラー関数に第 3 のパラメーターとして渡される request_info オブジェクトの getMode メソッドを呼び出すことによって、決定されます。要求が GET 要求である場合、呼び出し元は、ハンドラーに要求の OID に応じて request オブジェクトの value を設定するよう求めます。要求が GETNEXT 要求である場合、呼び出し元は、ハンドラーに要求の OID をツリー内で次に利用可能な OID に設定するよう求めます。以下のコードは、この例を示しています:
my $request;
my $string_value = "hello world";
my $integer_value = "8675309";

for($request = $requests; $request; $request = $request->next()) {
  my $oid = $request->getOID();
  if ($request_info->getMode() == MODE_GET) {
    if ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
      $request->setValue(ASN_OCTET_STR, $string_value);
    }
    elsif ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.1")) {
      $request->setValue(ASN_INTEGER, $integer_value);
    }
  } elsif ($request_info->getMode() == MODE_GETNEXT) {
    if ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
      $request->setOID(".1.3.6.1.4.1.8072.9999.9999.1.1");
      $request->setValue(ASN_INTEGER, $integer_value);
    }
    elsif ($oid < new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
      $request->setOID(".1.3.6.1.4.1.8072.9999.9999.1.0");
      $request->setValue(ASN_OCTET_STR, $string_value);
    }
  }
}
getModeMODE_GET を返す場合、ハンドラーは request オブジェクトの getOID 呼び出しの値を分析します。requestvalue は、OID が .1.0 で終わる場合は string_value に、OID が .1.1 で終わる場合は integer_value に設定されます。getModeMODE_GETNEXT を返す場合、ハンドラーは要求の OID が .1.0 かどうかを見つけ出し、その後 .1.1 に対する OID と値を設定します。ツリーで要求が .1.0 より高い場合は、.1.0 に対する OID と値が設定されます。実際、これはツリーの 次の 値を返すため、snmpwalk のようなプログラムは構造に関する事前知識なくツリーをトラバースできます。
変数のタイプは NetSNMP::ASN からの定数を使用して設定されます。利用可能な定数の全一覧については、NetSNMP::ASNperldoc を参照して下さい。
この例の Perl プラグインのコード全一覧は、以下のとおりです:
#!/usr/bin/perl

use NetSNMP::agent (':all');
use NetSNMP::ASN qw(ASN_OCTET_STR ASN_INTEGER);

sub hello_handler {
  my ($handler, $registration_info, $request_info, $requests) = @_;
  my $request;
  my $string_value = "hello world";
  my $integer_value = "8675309";

  for($request = $requests; $request; $request = $request->next()) {
    my $oid = $request->getOID();
    if ($request_info->getMode() == MODE_GET) {
      if ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
        $request->setValue(ASN_OCTET_STR, $string_value);
      }
      elsif ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.1")) {
        $request->setValue(ASN_INTEGER, $integer_value);
      }
    } elsif ($request_info->getMode() == MODE_GETNEXT) {
      if ($oid == new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
        $request->setOID(".1.3.6.1.4.1.8072.9999.9999.1.1");
        $request->setValue(ASN_INTEGER, $integer_value);
      }
      elsif ($oid < new NetSNMP::OID(".1.3.6.1.4.1.8072.9999.9999.1.0")) {
        $request->setOID(".1.3.6.1.4.1.8072.9999.9999.1.0");
        $request->setValue(ASN_OCTET_STR, $string_value);
      }
    }
  }
}

my $agent = new NetSNMP::agent();
$agent->register("hello_world", ".1.3.6.1.4.1.8072.9999.9999",
                 \&hello_handler);
プラグインをテストするには、上記のプログラムを /usr/share/snmp/hello_world.pl にコピーして、以下の行を /etc/snmp/snmpd.conf の設定ファイルに追加して下さい:
perl do "/usr/share/snmp/hello_world.pl"
新しい Perl プラグインをロードするためには、SNMP Agent Daemon を再起動する必要があります。再起動されると、snmpwalk が新しいデータを返すはずです:
~]$ snmpwalk localhost NET-SNMP-MIB::netSnmpPlaypen
NET-SNMP-MIB::netSnmpPlaypen.1.0 = STRING: "hello world"
NET-SNMP-MIB::netSnmpPlaypen.1.1 = INTEGER: 8675309
snmpget を使用すると、ハンドラーの他のモードを使用することも可能です:
~]$ snmpget localhost \
    NET-SNMP-MIB::netSnmpPlaypen.1.0 \
    NET-SNMP-MIB::netSnmpPlaypen.1.1
NET-SNMP-MIB::netSnmpPlaypen.1.0 = STRING: "hello world"
NET-SNMP-MIB::netSnmpPlaypen.1.1 = INTEGER: 8675309