NetApp作为专业存储,用起来还是比较让人放心的,不过放心不代表放手不管,一些重要的监控还是要做的。比如基本的CPU负载、网卡流量、磁盘使用率,作为数据存储特别关注的IOPS、DiskIO(因为有cache的原因,所以NetIO和DiskIO是不同时的,单从网卡进出不能判定磁盘的真实读写)。
从google中找到了NetApp的MIBtree,见https://support.ipmonitor.com/mibs/NETWORK-APPLIANCE-MIB/tree.aspx或http://www.mibdepot.com/cgi-bin/getmib3.cgi?abc=0&n=NETWORK-APPLIANCE-MIB&r=netapp&f=netapp_1_4.mib&t=tree&v=v1&i=0&obj=cp
(多贴一个,省的万一哪个站挂了……)
CPU等项没什么问题,问题在于IO的获取。
与普通服务器的流量一样,很明显的看到了miscNfsOps、miscNetRcvdKB和miscNetSentKB三个值。我们可以很简单的通过后个值的差值运算出速率。但如果单取一个值的时候,会发现一个很诡异的情况,见下:
# snmpwalk -v 1 -c public 10.10.10.118 .1.3.6.1.4.1.789.1.2.2.3 | awk ‘{print $NF}’
-1948753579
居然是个负值!
难道是对这个MIB的理解有问题?可通过如下命令计算的结果,和通过交换机端口获取的流量却基本符合了:
a=`snmpwalk -v 1 -c public 10.10.10.118 .1.3.6.1.4.1.789.1.2.2.3 | awk ‘{print $NF}’`;sleep 10;snmpwalk -v 1 -c public 10.10.10.118 .1.3.6.1.4.1.789.1.2.2.3 | awk ‘{print ($NF-”‘$a’”)*8/10″Kbps”}’
12364.8Kbps
虽然数值都变负了,但差量还是对的……汗,如果通过AVERAGE方式取这个差值绘图,倒不要紧,如果通过普通的流量Counter方式,这个图全反过X轴去了应该~
这个情况很容易让我想到用cacti获取网卡流量时的配置,如果选用默认的32bits绘图时,在流量较大时也会出现这类负值或者干脆画不出来的情况。
其次,DiskIO没有和Net一样的MIB;但net、disk和ops都有一对miscHigh***/miscLow***的数值。
这对值怎么用?MIB上的解释看起来超级茫然:
miscLowNfsOps:The total number of Server side NFS calls since the last boot. This object returns the least significant 32 bits of the value.
miscHighNfsOps:The total number of Server side NFS calls since the last boot. This object returns the most significant 32 bits of the value.
通过度娘了解了一下least significant bit和most significant bit的概念,原来LSB和MSB是在底层开发时的概念,因为2进制的字串比较长,所以通过MSB和LSB的命名方式来标明字串的高位(普通PC和MAC在存数据的时候顺序是反的,所以必须标记,还有其他原因,比如用MSB的1、0来表示正负数等)
那么这个most significant 32bits也就理解了~~就是从高位开始往低算的32位。least反过来……合并起来就是64位的完成数据了……2进制数的合并,也就是说用$MSBs * 2**32 + $LSBs。My God~
最后在zenoss的maillist上看到了相同的问题,其中网友的回答是:NetApp使用的snmp协议是v1版本,无法直接提供64bits的计数,只能变通一下,改用这种拆分方式了。
最后,举例一个监控ops的perl脚本。原脚本是centreon项目提供的,删除了一些和ops无关的语句。从中也可以学到Net::SNMP模块的使用,hash的解引用等~
#!/usr/bin/perl -w
use strict;
use Net::SNMP;
use Getopt::Long;
use lib “/usr/local/nagios/libexec”;
use utils qw(%ERRORS $TIMEOUT);
my $o_host = undef; # hostname
my $o_community = undef; # community
my $o_port = 161; # port
my $o_warn = undef; # warning limit
my $o_crit= undef; # critical limit
my $o_timeout= 10;
my $exit_code = undef;
my $o_type=undef;
my $output=undef;
my $o_perf= undef;
my %oids = (
‘cpuUsage’ => “.1.3.6.1.4.1.789.1.2.1.3.0”,
‘globalStatus’ => “.1.3.6.1.4.1.789.1.2.2.4.0”,
‘nfsHighOps’ => “.1.3.6.1.4.1.789.1.2.2.5.0”,
‘nfsLowOps’ => “.1.3.6.1.4.1.789.1.2.2.6.0”,
‘netRecHighBytes’ => “.1.3.6.1.4.1.789.1.2.2.11.0”,
‘netRecLowBytes’ => “.1.3.6.1.4.1.789.1.2.2.12.0”,
‘netSentHighBytes’ => “.1.3.6.1.4.1.789.1.2.2.13.0”,
‘netSentLowBytes’ => “.1.3.6.1.4.1.789.1.2.2.14.0”,
‘diskReadHighBytes’ => “.1.3.6.1.4.1.789.1.2.2.15.0”,
‘diskReadLowBytes’ => “.1.3.6.1.4.1.789.1.2.2.16.0”,
‘diskWriteHighBytes’ => “.1.3.6.1.4.1.789.1.2.2.17.0”,
‘diskWriteLowBytes’ => “.1.3.6.1.4.1.789.1.2.2.18.0”,
);
my @oidlist=($oids{nfsHighOps},$oids{nfsLowOps});
sub check_options {
Getopt::Long::Configure (“bundling”);
GetOptions(
‘H:s’ => $o_host, ‘hostname:s’ => $o_host,
‘p:i’ => $o_port, ‘port:i’ => $o_port,
‘C:s’ => $o_community, ‘community:s’ => $o_community,
‘c:s’ => $o_crit, ‘critical:s’ => $o_crit,
‘w:s’ => $o_warn, ‘warn:s’ => $o_warn,
# ‘T:s’ => $o_type,
);
}
########## MAIN #######
check_options();
# Connect to host
my ($session,$error);
#关键点:创建一个session连接被监控主机
($session, $error) = Net::SNMP->session(
-hostname => $o_host,
-community => $o_community,
-port => $o_port,
-timeout => $o_timeout
);
if (!defined($session)) {
printf(“ERROR: %s.n”, $error);
exit $ERRORS{“UNKNOWN”};
}
my $resultat=undef;
# Get rid of UTF8 translation in case of accentuated caracters (thanks to Dimo Velev).
#这里没太看懂perldoc,猜测是不开启MIB和oid的转换,这样输出结果比较简洁,不过注释掉这句运行结果毫无影响 $session->translate(Net::SNMP->TRANSLATE_NONE);
#取值的关键,get_request返回一个hash的引用。 #perldoc的原文是:“A reference to a hash is returned in blocking mode which contains the contents of the VarBindList. In non-blocking mode, a true value is returned when no error has occurred.” #get_request()中-callback和-delay是non-blocking模式的,而@oids是blocking模式。 #也就是说这个脚本里返回的是一个引用。
if (Net::SNMP->VERSION < 4) {
$resultat = $session->get_request(@oidlist);
} else {
$resultat = $session->get_request(-varbindlist => @oidlist);
}
if (!defined($resultat)) {
printf(“ERROR: Description/Type table : %s.n”, $session->error);
$session->close;
exit $ERRORS{“UNKNOWN”};
}
$session->close;
my $new_nfs_ops;
my $left_shift= 2**32;
my $last_nfs_ops = 0;
my $row ;
my $last_check_time ;
my $update_time;
my @last_values=undef;
my $flg_created = 0;
#解引用关键点:对%$resultat的解引用$$resultat{$oids{nfsHighOps}},其中$oids{nfsHighOps}是另一个hash——%oids中的值。 #可以采用foreach my $value ( values %$resultat ) { print “$valuen”; }的方式列出hash中的各个值。
#按照MSBs和LSBs的划分方法,通过2**32的方式合并得到64bits计数。
$new_nfs_ops= $$resultat{$oids{nfsHighOps}} * $left_shift + $$resultat{$oids{nfsLowOps}};
#输出到文本文件,因为是给nagios做监控脚本,所以必须通过差值的方式计算average型数值,而不像cacti绘图时那样可以直接传递counter型数值。 if (-e “/tmp/traffic_ops_”.$o_host) {
open(FILE,”<“.”/tmp/traffic_ops_”.$o_host);
while($row = <FILE>){
@last_values = split(“:”,$row);
$last_check_time = $last_values[0];
$last_nfs_ops = $last_values[1];
$flg_created = 1;
}
close(FILE);
} else {
$flg_created = 0;
}
$update_time = time();
unless (open(FILE,”>”.”/tmp/traffic_ops_”.$o_host)){
print “Check mod for temporary file : /tmp/traffic_ops_”.$o_host. ” !n”;
exit $ERRORS{“UNKNOWN”};
}
print FILE “$update_time:$new_nfs_ops:$new_cifs_ops”;
close(FILE);
if ($flg_created == 0){
print “First execution : Buffer in creation…. n”;
exit($ERRORS{“UNKNOWN”});
}
my $nfs_diff=$new_nfs_ops – $last_nfs_ops;
$nfs_diff=$new_nfs_ops if ($nfs_diff < 0);
my $time_diff=$update_time – $last_check_time;
$time_diff=$update_time if ($time_diff < 0);
my $nfs_ops = $nfs_diff / ( $time_diff );
printf(“Nfs ops : %.2f ops/sec “, $nfs_ops);
printf(“|nfsOps=”.$nfs_ops.”n”);
exit($ERRORS{“OK”});