muninを少し変更 その2

muninのソースコードって結構ガチガチに作ってあるところがある。
特にプラグインを実行する部分って/usr/sbin/munin-node っていう perlスクリプトなんだけど、
こんな感じ↓になって切り離し大変面倒。

/usr/sbin/sbin/munin-node より

sub run_service {
  my ($service,$command,$autoreap) = @_;
  $command ||="";
  my @lines = ();;
  my $timed_out = 0;
  if ($services{$service} and ($caddr eq "" or &has_access ($service))) {
    my $child = 0;
    my $timeout = get_var (\%sconf, $service, 'timeout');
    $timeout = $sconf{'timeout'} 
    	unless defined $timeout and $timeout =~ /^\d+$/;

    if ($child = open (CHILD, "-|")) {
      eval {
	  local $SIG{ALRM} = sub { $timed_out=1; die "$!\n"};
	  alarm($timeout);
	  while(<CHILD>) {
	    push @lines,$_;
	  }
      };

中略

    }
    wait;
    alarm(0);
  }
  else {
    print "# Unknown service\n";
  }
  chomp @lines;
  return (@lines);
}


sub process_request {
  my $self = shift;
  $caddr = $self->{server}->{peeraddr};
  print "# munin node at $FQDN\n";
  local $SIG{ALRM} = sub { logger ("Connection timed out."); die "timeout" };
  alarm($sconf{'timeout'});
  while( <STDIN> ){
    alarm($sconf{'timeout'});
    chomp;
    if (m/^list\s*([0-9a-zA-Z\.\-]+)?/) {
      &list_services($1);
    }
    elsif (/^quit/ || /^\./) {
      exit 1;
    }
    elsif (/^version/) {
      &show_version;
	}
    elsif (/^nodes/) {
      &show_nodes;
    }
    elsif (/^fetch\s?(\S*)/) {
      print_service (&run_service($1)) 
    }
    elsif (/^config\s?(\S*)/) {
      print_service (&run_service($1,"config"));
    } else  {
      print "# Unknown command. Try list, nodes, config, fetch, version or quit\n";
    }
  }
}

これをコピペして改変なんてダーティーなことはやりたくないし。
で、よく考えたら、この関数って引数は自由に渡せるようになっているから、ここに値を追加すればいいんだよ。
info って値を追加してみた。

sub process_request {
  my $self = shift;
  $caddr = $self->{server}->{peeraddr};
  print "# munin node at $FQDN\n";
  local $SIG{ALRM} = sub { logger ("Connection timed out."); die "timeout" };
  alarm($sconf{'timeout'});
  while( <STDIN> ){
    alarm($sconf{'timeout'});
    chomp;
    if (m/^list\s*([0-9a-zA-Z\.\-]+)?/) {
      &list_services($1);
    }
    elsif (/^quit/ || /^\./) {
      exit 1;
    }
    elsif (/^version/) {
      &show_version;
	}
    elsif (/^nodes/) {
      &show_nodes;
    }
    elsif (/^fetch\s?(\S*)/) {
      print_service (&run_service($1)) 
    }
    elsif (/^config\s?(\S*)/) {
      print_service (&run_service($1,"config"));
    }
    elsif (/^info\s?(\S*)/) {
      print_service (&run_service($1,"info"));
    } else  {
      print "# Unknown command. Try list, nodes, config, fetch, version, info or quit\n";
    }
  }
}


んで、こんなプラグインを作った。
ニートプラグイン

/usr/share/munin/plugins/neet

#!/bin/sh
#
# neet plugin
#
# Parameters:
#
#       info
#

[ "$commands"  ] || commands="ps aux ; free ; df -h ; tail --lines 100 /var/log/messages ; dmesg | tail --lines 100 ; netstat -np"


if [ "$1" = "autoconf" ]; then
        # neet not work.
        echo no
        exit 1
fi

if [ "$1" = "config" ]; then
        exit 1
fi

if [ "$1" = "info" ]; then
        OLDIFS=IFS
        IFS=";"
        for command in $commands  ; do
                echo "=============================================="
                echo "command: $command"
                bash -c $command
                echo "=============================================="
        done
        IFS=OLDIFS

        exit 0
fi

exit 1

名前のごとくニートなので、グラフも書かなければ基本何もしない。
だけど、infoって名前で呼ばれたときだけ本気出して仕事する。
ニートは普段休んでいるだけで実力を出すとすごいんだ。三年寝たろう的な感じ。
アニメによるある何のとりえもない男が女の子を守るために本気出して世界を救っちゃってハーレムエンドになる感じ。

これでニートに対して設定を行っておければあらかじめ登録していたコマンドが発行できるよ。
ディフォルトでも結構な情報が取れるとは思うけどね。

んで、メールが送信されるときに、こんな感じで 外の muninにリクエストを投げるといいよ。
http://goungoun.dip.jp/app/fswiki/wiki.cgi/debianetch?page=2007%2F09%2F27%A1%A2munin%A1%A2munin-limits%20%A4%C7%A5%E1%A1%BC%A5%EB%C4%CC%C3%CE に掲載されているコマンドを改変しています。

vi /etc/munin/munin.conf
contact.email.command /usr/local\/sbin/mailmessage.sh "${var:host}" "Munin-notification for ${var:group} ::${var:host}" nareka@example.com.local


メール送信プログラムはこんな感じ /usr/local/sbin/mailmessage.sh です。

#!/bin/bash

if [ $# -ne 3 ]; then
        echo "Usage:"
        echo "$0 remote_munin_ip mailsubject mailaddress"
        echo ""
        echo "Ex"
        echo " echo "body" | $0 127.0.0.1     'server alert!' nareka@example.com.local"
        echo " echo "body" | $0 192.168.1.200 'server alert!' nareka@example.com.local"
        exit 1
fi

HOST=$1
PORT=4949

echo "read stdin..."
BODY=""
while read LINE
do
        BODY="$BODY$LINE\n"
done

echo "sendmail..."
#cat <<EOM | mail -s "$2" $3
cat <<EOM
$body
---
`( sleep 2 ;echo "info neet" ;sleep 3 ) | telnet $HOST $PORT`
EOM


普段でも、このサーバの詳細情報みてーとおもったら、こんな感じでコマンド発行すればいつでも見れるよ。

( sleep 2 ;echo "info neet" ;sleep 3 ) | telnet サーバ名 4949

いちいち該当サーバにログインなんてしなくてもok。楽チンですね。


ついでに、この前の web_access にプロキシ機能を追加してみた。
これで、プロキシを経由した外回りからのチェックもできるようになったよー。

/usr/share/munin/plugins/web_access

#!/usr/bin/perl
#
# Parameters supported:
#
# 	config
# 	autoconf
#
# hide option
#
#   autoconfigfile
#         ---> /etc/munin/plugin-conf.d/munin-node auto make.

=head1 NAME

web_accesses is web health check plugin.
INSPIRE wget_page.
This source was made based on apache_accesses. 

This configuration section shows the defaults of the plugin:

  [web_accesses]
     env.web0_url http://127.0.0.1/
     env.web0_host myhost.local
     env.web0_grep <html>
     env.web0_proxy http://192.168.1.1:8080
     env.web0_proxy_auth userid:password

     env.web1_url https://127.0.0.1/
     env.web1_host secure.myhost.local
     env.web1_grep <html>

     env.web2_url https://127.0.0.1/
     env.web2_host super.myhost.local

		<snip>

     env.web99_url https://127.0.0.1:8080/
     env.web99_grep <html>


env.web0_host and env.web0_grep is option paramater.
The graph can hang by the response speed. 
The time-out is 60 seconds. 

The figure of 61 abnormalities is returned when becoming an error. 

61   timeout or server status error (404 403 ... more)
62   When contents specified for grep are not included.

health check ga dekiruyo!
yattane taechan!!

=head1 BUGS

Does not support digest authentication.

=head1 AUTHOR

rti
This source was made based on apache_accesses. 


=head1 LICENSE

GPLv2

=cut


my $ret = undef;

if (! eval "require LWP::UserAgent;")
{
	$ret = "LWP::UserAgent not found";
}
# for ssl
eval "require Crypt::SSLeay;";

if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" )
{
	if ($ret)
	{
		print "no ($ret)\n";
		exit 1;
	}
	if ( length(getHttpdSOption()) <= 0 )
	{
		print "no (httpd)\n";
		exit 1;
	}
	print "yes\n";
	exit 0;
}


#hide option.
if ( defined $ARGV[0] and $ARGV[0] eq "autoconfigfile" )
{
	# httpd -S
	my $virtualhosts = getHttpdSOption();
	if ( length($virtualhosts) <= 0 )
	{
		print "no (httpd)\n";
		exit 1;
	}
	print "[web_accesses]\n";

	my $scanmode = 0;
	my $i = 0;
	foreach my $line (split("\n",$virtualhosts))
	{
		if ($scanmode == 0)
		{
			if (    $line =~ /^VirtualHost configuration/im
			     || $line =~ /^wildcard NameVirtualHosts/im)
			{
				$scanmode = 1;
			}
		}
		elsif ($scanmode == 1)
		{
			#VirtualHost configuration
			if (   $line =~ /^.*?:([0-9]+?) +?([0-9a-zA-Z\.\-_]+?) /im
				|| $line =~ /^ .*?port ([0-9\*]+?) +?namevhost ([0-9a-zA-Z\.\-_]+?) /im)
			{
				my $protocol = "http";
				my $port = $1;
				my $host = $2;
				if ($port == "*") 
				{
					$port = 80;
				}
				if ($port == 443)
				{
					$protocol = "https";
				}
				print "env.web${i}_url $protocol://127.0.0.1:$port/\n";
				print "env.web${i}_host $host\n";
				print "#env.web${i}_ua mozilla\n";
				print "#env.web${i}_grep <html>\n";
				print "#env.web${i}_proxy http://192.168.1.1:8080\n";
				print "#env.web${i}_proxy_auth userid:password\n";
				$i ++;
			}
		}
	}
	exit;
}






if ( defined $ARGV[0] and $ARGV[0] eq "config" )
{
	print "graph_title Web Access Time\n";
	print "graph_args --base 1000 --lower-limit -10 --upper-limit 70\n";
	print "graph_vlabel sec of web access\n";
	print "graph_category HealthCheck\n";
	print "graph_info web access time\n";


	for(my $i = 0; $i < 100 ; $i ++ )
	{
		if (!exists $ENV{"web${i}_url"})
		{
			next;
		}
		my $url = $ENV{"web${i}_url"};
		my $host = "web{$i}";
		if (exists $ENV{"web${i}_host"})
		{
			$host = $ENV{"web${i}_host"};
		}
		print "web${i}.label $host\n";
		print "web${i}.info $host($url)\n";
		print "web${i}.type gauge\n";
		print "web${i}.draw LINE2\n";
		print "web${i}.max 70\n";
		print "web${i}.min -10\n";
		print "web${i}.warning 50\n";
		print "web${i}.critical 60\n";

	}

	exit 0;
}


for(my $i = 0; $i < 100 ; $i ++ )
{
	if (!exists $ENV{"web${i}_url"})
	{
		next;
	}

	my $ua = LWP::UserAgent->new(timeout => 60);
	my $request = HTTP::Request->new('GET',$ENV{"web${i}_url"});

	if (exists $ENV{"web${i}_host"})
	{
		$ua->default_header("Host"=>$ENV{"web${i}_host"});
	}
	if (exists $ENV{"web${i}_ua"})
	{
		$ua->agent($ENV{"web${i}_ua"});
	}

	#proxy and proxy auth support.
	if (exists $ENV{"web${i}_proxy"})
	{
		$ua->proxy('http', $ENV{"web${i}_proxy"});

		if (exists $ENV{"web${i}_proxy_auth"})
		{
			my $auth_sep = index($ENV{"web${i}_proxy_auth"} , ":");
			if ($auth_sep >= 1)
			{
				$request->proxy_authorization_basic( 
					substr($ENV{"web${i}_proxy_auth"},0 , $auth_sep) ,
					substr($ENV{"web${i}_proxy_auth"},$auth_sep + 1) );
			}
		}
	}
	
	
	#redirect off.
	$ua->requests_redirectable([]);
	my $requesttime = time;
	my $response = $ua->request($request);
	$requesttime = time - $requesttime;
	
	if ( $response->code <= 199 || $response->code >= 400 )
	{
		#bad respons code
		$requesttime = 61;
	}
	else
	{
		if (exists $ENV{"web${i}_grep"})
		{
			my $grep = $ENV{"web${i}_grep"};
			if ( ! ($response->content =~ /$grep/im) ) 
			{
				#bad respons contents
				$requesttime = 62;
			}
		}
	}
	print "web${i}.value $requesttime\n";
}


#`httpd -S` result.
sub getHttpdSOption()
{
	my $virtualhosts = `httpd -S 2>&1`;
	if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	my $virtualhosts = `/usr/sbin/httpd -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	
	#for debian
	$virtualhosts = `apache2 -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	#for source build
	$virtualhosts = `/usr/local/apache2/sbin/apache -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	#not found...
	return "";
}

# vim:syntax=perl

あとは、設定の簡易化ツールとメンテナンス時間帯の設定がほしいんだけど、、、
どうしたもんかねー。

メンテナンスの時間帯は何とかなるとして、設定の簡易化はどうしたものか。
webベースのUIが理想なんだけど、、、、 muninのファイルは root権限で作られているから手が出せぬ。
sudo するなんてイヤだし。。。