最近ではエージェントレス監視が出来る製品が増えてきている。
JP1もエージェントレス監視が出来るようになった。
以前、『【コラム】エージェントレス監視』でも書いたように、エージェントレス監視を行う方法は2つだ。
SNMPを利用したエージェントレス方式とTelnetやSSHなどを使用したエージェントレス方式だ。
今回は、Telnetを使用したエージェントレス方式での監視プラグインの作り方を紹介する。
Nagiosプラグイン開発ガイドライン』も参考にしてもらいたい。
 



 

Telnetを使用したエージェントレス方式での監視プラグインの基本仕様

Telnetを使用したエージェントレス方式での監視プラグインは、結局のところ、人間がTelnetで監視対象サーバにログインして、必要な情報を知るためにコマンドを実行するのと同じことをするプログラムだ。
だから最低限、以下の情報が必要だ。
 ①監視対象ホストのIPアドレス
 ②ログインユーザ名
 ③ログインパスワード
 
また、Nagiosプラグインとして以下の引数も用意する必要があるだろう。
 ④WARNINNG閾値
 ⑤CRITICAL閾値
 ⑥タイムアウト値
 
さらに、『Nagiosプラグイン開発ガイドライン』では、以下の引数も用意するようにとなっている。
 ⑦ヘルプ表示
 ⑧バージョン情報の表示
 ⑨デバッグ用の情報出力
 
そして、以下の処理が必要となる。
1.Telnetで監視対象サーバにログインする
2.監視情報取得のためのコマンドの実行
3.監視対象サーバからログアウトする
4.コマンドの実行結果から監視データを取得
5.監視結果の判定
6.ステータス情報とパフォーマンス情報の作成
7.ステータス情報とパフォーマンス情報の出力
8.終了処理(監視結果をNagiosに戻す)
 

システム要求

今回はPerlで作るため、以下のPerlモジュールが必要だ。
 Net::Telnet
 

# yum install perl-Net-Telnet

 

Telnetで監視対象サーバにログインする方法

Telnetで監視対象サーバにログインするためには、上記のPerlモジュール Net::Telnet を使用するために以下の宣言が必要だ。
 
use Net::Telnet;
 
次に、new Net::Telnet()を使用するが、この際、以下の値を決める必要がある。
 ①タイムアウト値(Timeout)
 ②ログインユーザのプロンプト(Prompt)
 ③エラーモード(Errmode)
 
タイムアウト値(Timeout)は、引数で指定された値を使えばいいだろう。
ログインユーザのプロンプト(Prompt)は、正規表現で記述しておいたほうがいいだろう。
そのためには、まず、ログインユーザのシェルを決める必要がある。
以下のケースはログインユーザのシェルがcshのケースだ。
エラーモード(Errmode)は、各Telnet処理のエラーステータスを呼び出したプラグイン側でハンドリングするために”return”を選択する。
 

my $telnet = new Net::Telnet(
	Timeout => $opt_t,
	Prompt => '/.*\% $/',	# CSHの場合
	Errmode => "return"
);
$telnet->open($host);
# Login
$telnet->login($user, $passwd);

 

監視情報取得のためのコマンドを実行する方法

ログインできたら、必要な情報を得るためのコマンドを実行する。
どんな監視を行うのかによって、必要な情報を得るために実行するコマンドが異なる。
この部分は、作成するプラグインごとに作りこんでいく必要があるが、コマンド実行部分はいたってシンプルだ。
また、取得した情報を処理しやすいように環境変数LANGをCに統一しておくと便利だ。
以下のケースはログインユーザのシェルがcshのケースだ。
 

# set LANG
$telnet->cmd("setenv LANG C");

# 情報取得のためのコマンドの実行
@buff = $telnet->cmd("実行コマンド");

 
もちろん、同じことをするにもOSごとにコマンド体系は異なる。
それに対応するためには、OSごとにコマンド処理部分を作らなければならない。
それが、複数OSに対応するということだ。
以下のように監視対象のOS情報を取得することで、複数OSに対応する処理を書く事が可能だ。
 

# set LANG
$telnet->cmd("setenv LANG C");

# get OS name
my ($OS) = $telnet->cmd("uname");
chomp($OS);
if ( $OS eq "SunOS" ) {
	$CMD = "Solaris用の実行コマンド";
} elsif ( $OS eq "Linux" ) {
	$CMD = "RedHat系Linux用の実行コマンド";
} else {
	print "ERROR: Could not check OS:$OS\n";
	exit $ERRORS{'UNKNOWN'};
}

# 情報取得のためのコマンドの実行
@buff = $telnet->cmd("$CMD");

 
情報を取得したら監視対象サーバからログアウトする。
 

# Telnetログアウト
$telnet->close;

 

コマンドの実行結果から監視データを取得する方法

これも上記で実行したコマンドによって異なる処理なのだが、基本はコマンドの実行で配列(@buff)に読み込んだコマンドの実行結果から以下の情報を取得することだ。
1つは、監視結果の判定に必要となる監視データ。
もう1つは、ステータス情報とパフォーマンス情報の作成に必要な情報だ。
 

# コマンドの出力結果から監視データを取得
foreach (@buff) {
	# 監視データ($data)を取得
	# 処理内容は実行したコマンドによって異なる
	# ステータス情報の作成に必要な情報を取得
	# パフォーマンス情報の作成に必要な情報も取得
}

 

監視結果の判定方法

表示された情報から必要な情報を抜き取ったら、閾値を超えていないかどうか判定する必要がある。
閾値の判定条件は、作成するプラグインの仕様によって異なるが、たいていは閾値を超えているかどうかの判定を行うことになる。
以下は、その一例だ。
 

# 監視データ($data)から監視結果を判定する
if ($data < $warn_v) {
	if ( $status <= $ERRORS{'OK'} ) {
		$status = $ERRORS{'OK'};
		$statdata = "OK: $data";
	}
} elsif ($data >= $warn_v  && $data < $crit_v) {
	if ( $status <= $ERRORS{'WARNING'} ) {
		$status = $ERRORS{'WARNING'};
		$statdata = "WARNING: $data (threshold warnning = $warn_v)";
	}
} else {
	if ( $status <= $ERRORS{'CRITICAL'} ) {
		$status = $ERRORS{'CRITICAL'};
		$statdata = "CRITICAL: $data (threshold critical = $crit_v)";
	}
}
$perfdata = "パフォーマンス情報";

 
この例では、ステータス情報($statdata)とパフォーマンス情報($perfdata)も一緒に作成している。
$warn_vと$crit_vは、それぞれWARNING閾値とCRITICAL閾値だ。
連想配列%ERRORSは、Nagios Pluginsのライブラリ(use lib "/usr/local/nagios/libexec" ;)で提供される。
 

ステータス情報とパフォーマンス情報の出力および終了処理(監視結果をNagiosに戻す)

次は、ステータス情報とパフォーマンス情報を標準出力に出力し、監視ステータスをNagiosへ戻して終了する。
ステータス情報とパフォーマンス情報の出力仕様については、『Nagiosプラグイン開発ガイドライン』を参照してもらいたい。
 

print "$statdata | $perfdata\n";
exit $status;

 

引数処理

プラグインに渡す引数の処理も考える必要がある。
ここでは、『Telnetを使用したエージェントレス方式での監視プラグインの基本仕様』のところで書いた以下の引数の処理を考える。
 ①監視対象ホストのIPアドレス
 ②ログインユーザ名
 ③ログインパスワード
 ④WARNINNG閾値
 ⑤CRITICAL閾値
 ⑥タイムアウト値
 ⑦ヘルプ表示
 ⑧バージョン情報の表示
 ⑨デバッグ用の情報出力
 
引数の処理には、Getopt::Longを使用する。
また、引数には『Nagiosプラグイン開発ガイドライン』推奨のNagios Plugin標準オプションというものがある。
以下はその一例だ。
 

use Getopt::Long;

Getopt::Long::Configure('bundling');

sub process_arguments(){
	GetOptions
		("h"   => \$opt_h, "help"		=> \$opt_h,		# Nagios Plugin標準オプション
		 "V"   => \$opt_V, "version"		=> \$opt_V,		# Nagios Plugin標準オプション
		 "H=s" => \$opt_H, "hostname=s"		=> \$opt_H,		# Nagios Plugin標準オプション
		 "u=s" => \$opt_u, "username=s"		=> \$opt_u,
		 "p=s" => \$opt_p, "password=s"		=> \$opt_p,
		 "w=f" => \$opt_w, "warning=f"		=> \$opt_w,		# Nagios Plugin標準オプション
		 "c=f" => \$opt_c, "critical=f"		=> \$opt_c,		# Nagios Plugin標準オプション
		 "t=i" => \$opt_t, "timeout=i"		=> \$opt_t,		# Nagios Plugin標準オプション
		 "v"   => \$opt_v, "verbose"		=> \$opt_v		# Nagios Plugin標準オプション
		 );

	if ($opt_h) {			# ⑦ヘルプ表示
		print_help();
		exit $ERRORS{'OK'};
	}

	if ($opt_V) {			# ⑧バージョン情報の表示
		print_revision($PROGNAME,'$Revision: 1 $ ');
		exit $ERRORS{'OK'};
	}

	unless (defined $opt_H) {	# ①監視対象ホストのIPアドレス
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	} else {
		$host = $opt_H;
	}

	if (defined $opt_u && defined $opt_p) {
		$user = $opt_u;		# ②ログインユーザ名
		$passwd = $opt_p;	# ③ログインパスワード
	} else {
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	}
	
	unless ( defined $opt_w && defined $opt_c ) {
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	} else {
		$warn_v = $opt_w;	# ④WARNINNG閾値
		$crit_v = $opt_c;	# ⑤CRITICAL閾値
	}

	if ( $warn_v >= $crit_v) {
		print "Warning($warn_v) cannot be greater than Critical($crit_v)!\n";
		exit $ERRORS{'UNKNOWN'};
	}

	unless (defined $opt_t) {	# ⑥タイムアウト値
		$opt_t = $utils::TIMEOUT;
	}

	if (defined $opt_v ){		# ⑨デバッグ用の情報出力
		$verbose = $opt_v;
	}

	return $ERRORS{'OK'};
}

 

ヘルプ表示とUsage表示

この機能は直接必須ではないが、あると便利だろう。
Nagiosプラグイン開発ガイドライン』でも推奨されている。
以下の例では、WARNINNG閾値とCRITICAL閾値はdouble型という想定だ。
 

sub print_help () {
	print "\n";
	print_revision("プラグイン名",'$Revision: 1 $');
	print "\n";
	print_usage();
	print "\n";
	print "
Options:
 -h, --help
    help information
 -V, --version
    version information
 -H, --hostname=ADDRESS
    Host name, IP Address
 -u, --username = STRING
    login username
 -p, --password = STRING
    login password
 -w, --warning=DOUBLE
    a warning will be generated
 -c, --critical=DOUBLE
    a critical will be generated
 -t, --timeout=INTEGER
    connection times out(second) (default: $utils::TIMEOUT)
 -v, --verbose
    Show details for debugging
	\n\n";
	
}

sub print_usage () {
	print "Usage: プラグイン名 -H host -u username -p password\n";
	print " [-w ] [-c ] [-t ] [-v verbose]\n";
}


 

まとめ

以上から以下のような雛形が考えられる。
 

#! /usr/bin/perl -w
#
############################################################################
# 作成日:2011年9月17日(土)
# 作成者:Jラボ(Junrei)
# --------------------------------------------------------------------------
# 改修履歴
############################################################################

use POSIX;
use strict;
use Getopt::Long;
use Net::Telnet;
use lib  "/usr/local/nagios/libexec" ;
use utils qw(%ERRORS &print_revision);
use vars qw($PROGNAME);

use vars qw($opt_h $opt_V $opt_H $opt_u $opt_p $opt_w $opt_c $opt_t $opt_v);
use vars qw($host $user $passwd $warn_v $crit_v $verbose);

sub print_help ();		# ヘルプ表示関数
sub print_usage ();		# Usage表示関数
sub process_arguments ();	# 引数処理用関数

$PROGNAME = "プログラム名";

# 引数処理
Getopt::Long::Configure('bundling');
my ($status) = process_arguments();
if ($status){
	print "ERROR: processing arguments\n";
	exit $ERRORS{"UNKNOWN"};
}

my (@buff) = undef;
my ($statdata) = "";
my ($perfdata) = "";
my ($data) = 0;
my ($result);

# タイムアウト処理
$SIG{'ALRM'} = sub {
	print ("ERROR: timed out\n");
	exit $ERRORS{"WARNING"};
};
alarm($opt_t);

# Telnetログイン
my $telnet = new Net::Telnet(
	Timeout => $opt_t,
	Prompt => '/.*\% $/',	# CSHの場合
	Errmode => "return"
);
$result = $telnet->open($host);
unless ($result) {
	$state = $ERRORS{'CRITICAL'};
	$statdata = "CRITICAL: Unable to open TCP socket.($host open error.)";
	print "$statdata\n";
	exit $state;
}
# Login
$result = $telnet->login($user, $passwd);
unless ($result) {
	$state = $ERRORS{'CRITICAL'};
	$statdata = "CRITICAL: Unable to login.(user=$user, passwd=$passwd login error.)";
	print "$statdata\n";
	exit $state;
}
# set LANG
$telnet->cmd("setenv LANG C");

@buff = $telnet->cmd("実行コマンド");

# Logout
$telnet->close;

# コマンドの出力結果から監視データを取得
foreach (@buff) {
	# 監視データ($data)を取得
	# 処理内容は実行したコマンドによって異なる
	# ステータス情報の作成に必要な情報を取得
	# パフォーマンス情報の作成に必要な情報も取得
}

# 監視データ($data)から監視結果を判定する
if ($data < $warn_v) {
	if ( $status <= $ERRORS{'OK'} ) {
		$status = $ERRORS{'OK'};
		$statdata = "OK: $data";
	}
} elsif ($data >= $warn_v  && $data < $crit_v) {
	if ( $status <= $ERRORS{'WARNING'} ) {
		$status = $ERRORS{'WARNING'};
		$statdata = "WARNING: $data (threshold warnning = $warn_v)";
	}
} else {
	if ( $status <= $ERRORS{'CRITICAL'} ) {
		$status = $ERRORS{'CRITICAL'};
		$statdata = "CRITICAL: $data (threshold critical = $crit_v)";
	}
}
$perfdata = "パフォーマンス情報";


if ( $? ) {
	$status = $status == $ERRORS{"CRITICAL"} ? $ERRORS{"CRITICAL"} : $ERRORS{"UNKNOWN"}  ;
}

print "$statdata | $perfdata\n";
exit $status;


#####################################

sub process_arguments(){
	GetOptions
		("h"   => \$opt_h, "help"		=> \$opt_h,		# Nagios Plugin標準オプション
		 "V"   => \$opt_V, "version"		=> \$opt_V,		# Nagios Plugin標準オプション
		 "H=s" => \$opt_H, "hostname=s"		=> \$opt_H,		# Nagios Plugin標準オプション
		 "u=s" => \$opt_u, "username=s"		=> \$opt_u,
		 "p=s" => \$opt_p, "password=s"		=> \$opt_p,
		 "w=f" => \$opt_w, "warning=f"		=> \$opt_w,		# Nagios Plugin標準オプション
		 "c=f" => \$opt_c, "critical=f"		=> \$opt_c,		# Nagios Plugin標準オプション
		 "t=i" => \$opt_t, "timeout=i"		=> \$opt_t,		# Nagios Plugin標準オプション
		 "v"   => \$opt_v, "verbose"		=> \$opt_v		# Nagios Plugin標準オプション
		 );

	if ($opt_h) {
		print_help();
		exit $ERRORS{'OK'};
	}

	if ($opt_V) {
		print_revision($PROGNAME,'$Revision: 1 $ ');
		exit $ERRORS{'OK'};
	}

	unless (defined $opt_H) {
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	} else {
		$host = $opt_H;
	}

	if (defined $opt_u && defined $opt_p) {
		$user = $opt_u;
		$passwd = $opt_p;
	} else {
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	}
	
	unless ( defined $opt_w && defined $opt_c ) {
		print_usage();
		exit $ERRORS{'UNKNOWN'};
	} else {
		$warn_v = $opt_w;
		$crit_v = $opt_c;
	}

	if ( $warn_v >= $crit_v) {
		print "Warning($warn_v) cannot be greater than Critical($crit_v)!\n";
		exit $ERRORS{'UNKNOWN'};
	}

	unless (defined $opt_t) {
		$opt_t = $utils::TIMEOUT;	# default timeout
	}

	if (defined $opt_v ){
		$verbose = $opt_v;
	}

	return $ERRORS{'OK'};
}

sub print_usage () {
	print "Usage: $PROGNAME -H host -u username -p password\n";
	print " [-w ] [-c ] [-t ] [-v verbose]\n";
}

sub print_help () {
	print "\n";
	print_revision($PROGNAME,'$Revision: 1 $');
	print "\n";
	print_usage();
	print "\n";
	print "
Options:
 -h, --help
    help information
 -V, --version
    version information
 -H, --hostname=ADDRESS
    Host name, IP Address
 -u, --username = STRING
    login username
 -p, --password = STRING
    login password
 -w, --warning=DOUBLE
    a warning will be generated
 -c, --critical=DOUBLE
    a critical will be generated
 -t, --timeout=INTEGER
    connection times out(second) (default: $utils::TIMEOUT)
 -v, --verbose
    Show details for debugging
	\n\n";
	
}



 

 


Categories: Nagios ,Nagios Plugins


Leave a Reply