perl使用snmp协议监控网口流量
最近部分主机出现了短时流量异常,导致了一些相关联的服务器受到影响。Nagios对机器的流量报警不能生效,于是决定写一个脚本来监控部分主机和交换机的流量情况,当出现流量超出定义后,向系统管理员发送警报以并采取措施来处理。技术设计:接之前的架构,通过snmp方式取得机器的运行信息,并从中取出网络接口(端口)流量值,经过相应处理后存档,当下次取得数据时,做比较并发送报警。
这里需要注意与snmp有关的细节问题:
主机和交换机有多个接口,不可能要全部监控。只能监控指定接口(像主机的外网口,重要业务的内网口,交换机的上连端口)。
主机和交换机流量计数单位不一致,需要分开处理(主机返回的是'byte',而交换机是'bit')。
需要分别对不同主机(业务)做速率监控(交换机肯定要比主机的外网口的量大,且受制于idc提供的带宽)。
32位系统计数重置(主要表现在主机上,32的操作系统网口的流量满4g后会从0开始重新计数)。
所取得到接口值是自启动或重新计数以来,流量经它的数据量,而非速率!
这里在配置文件上下了一些功夫,首先设定通用配置,然后按不同主机设定不同参考值,而不是在程序层面上处理这些不同之处;这样脚本在编写时考虑较少,也容易看懂。
配置文件里所写的单位是电信制的:Mbps,所有脚本在比较时会将其转换为统一制单位。由于不需要对数据进行存档,因此不对产生数据做保留,仅对上次取到时值做保存,并在脚本结束时当本次的数据记录取代上一次的,使用了Storable模块来存放数据,具体在存放文件为'snmp'。
配置文件示例如下,这里使用了yaml格式,关于这种格式文件的说明,请参考:perl合并yaml继承key
---
default: &std
ipaddr: '127.0.0.1'
type: 'host'
interface: ['eth0']
tels: ['131620800xx','159019941yy']
misc: {'rate': '5','period': [7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22],'unit': 'm'}
threshold: {'in': 10,'out': 10}
hosts: ['sb83','sb84','zhru34']
sb83:
<<: *std
ipaddr: '114.80.x.83'
sb84:
<<: *std
ipaddr: '114.80.x.84'
zhru34:
<<: *std
ipaddr: '61.129.x.34'
type: 'switch'
threshold: {'in': 45,'out': 45}
interface: ['GigabitEthernet0/1']
说明:
default为所有主机默认的配置文件,其中定义了一组手机号码用于来发送报警信息,默认的的类型为主机,接口为'eth0',threshold定义了进出口的流量阀值,misc中定义了脚本发送信息的时间段(period),工作的时间间隔(5m)分钟。
下面定义了被监控的主机和交换机,显然ip地址是它们的提供的,zhru34是一台交换机,与上面的主机有较大的区别。
脚本:
use Net::SNMP::Interfaces;
use Storable;
use YAML::XS;
use Data::Dumper;
use Cwd qw(abs_path realpath);
use File::Basename qw(dirname);
$\="\n";
chdir(dirname(abs_path($0)));
my $mainconf=YAML::XS::LoadFile('/var/www/config/traffic.yaml');
my ($dbfile,$h,$s,$lasth,$msg)=('snmp');
#get current hour
my ($cm,$ch,$cd) = (localtime(time))[1,2,3];
#get config
sub get_main_qsh{
my $qsh=shift;
return $mainconf->{$qsh};
}
#merge yaml file
sub merge{
my $rc=shift;
my $h=();
my %pm=%{$rc->{'<<'}};
for(keys %pm){
$h->{$_}=$pm{$_};
}
delete $rc->{'<<'};
$h->{$_}=$rc->{$_} for( keys %{$rc});
return $h;
}#fun merge end
my ($tels,$i,@rate)=('',0);
my $conf=get_main_qsh('default');
$tels.=$_.',' for(@{$conf->{tels}});
chop($tels);
my $period=$conf->{misc}->{period};
my $rate=$conf->{misc}->{rate};
exit unless grep( /^$ch$/, @{$period} );
while(1){
last if($i*$rate > 60);
push(@rate,$i*$rate);
$i++;
}
#print Dumper(\@rate);
exit unless grep( /^$cm$/, @rate );
#my $in=Net::SNMP::Interfaces->new(Hostname =>$host,Community => 'public' );
#my @ifnames=$in->all_interfaces();
#print Dumper(@ifnames);
#get current time st
$h->{dt}=time();
#handle each host one by one
foreach my $host (@{get_main_qsh('hosts')}){
my $hconf=get_main_qsh($host);
my $ip=$hconf->{ipaddr};
$h->{$host}->{ip}=$ip;
my $in=Net::SNMP::Interfaces->new(Hostname=>$ip,Community=>'public');
my $s=merge($hconf);
foreach my $inter (@{$s->{interface}}){
$h->{$host}->{$inter}->{in}=(($s->{type}=='host')?$in->ifInOctets($inter)*8:$in->ifInOctets($inter));
$h->{$host}->{$inter}->{in_limit}=$s->{threshold}->{in};
$h->{$host}->{$inter}->{out}=(($s->{type}=='host')?$in->ifOutOctets($inter)*8:$in->ifOutOctets($inter));
$h->{$host}->{$inter}->{out_limit}=$s->{threshold}->{out};
}
}
#get storable var
if(-e $dbfile){
$lasth=retrieve($dbfile);
}else{
store($h,$dbfile);
exit;
}
#print Dumper($lasth,$h);
#the time scope
my $scope=($h->{dt}-$lasth->{dt});
foreach my $host (@{get_main_qsh('hosts')}){
for (keys %{$h->{$host}}){
#print $h->{$host}->{$_}->{in} unless /ip/;
checkrs($h->{$host}->{ip},'in',$_,$h->{$host}->{$_}->{in},$lasth->{$host}->{$_}->{in},$h->{$host}->{$_}->{in_limit}) unless /ip/;
checkrs($h->{$host}->{ip},'out',$_,$h->{$host}->{$_}->{out},$lasth->{$host}->{$_}->{out},$h->{$host}->{$_}->{out_limit}) unless /ip/;
}
}
#check the host interface flow is beyond the defined vaule
sub checkrs{
my ($host,$dir,$inter,$cur,$las,$threshold)=(@_);
my ($cuspeed)=(int(($cur-$las)/(1024*1024*$scope)));#convert to tel unix Mbps
if($cuspeed > $threshold){
#$msg.="$host-$dir-speed:$cuspeed-over:$threshold!";
$msg.="$host-$dir-$cuspeed!";
# print $msg;
}
}
store($h,$dbfile);
print $msg if($msg);
---------------
#这里是一个调用外部的短信发送脚本来发信息
sub mobilesnd{
my ($tels,$msg)=@_;
return unless($msg);
my @args = ("/usr/local/php/bin/php /var/www/bin/sendsms/sendsms.php freeoa $tels \"$msg\"");
system(@args) == 0 or die "system @args failed: $?"
}
mobilesnd($tels,$msg);
---------------
参考链接:
认识网络速率单位
使用snmp取得主机网络流量信息