Здесь я расскажу, как и что можно в nagios мониторить в vmware, CMC-TC, Synology, ИБП (APC и Chloride), принтерах и о мониторинге интерфейсов в Cisco по именам и зачем это нужно.
Nagios и VMWARE
У нас стоят кластеры vmware из vSphere 5.1, лицензионные, с техподдержкой. Хотя техподдержка vmware — это отдельно и не к ночи. По snmp там можно мониторить стандартную ветку HOST-RESOURCES-V2-MIB (oid 1.3.6.1.2.1.25), там как обычно много интересного, память, процессора, сеть. Насчет сети есть оговорка — версии vSphere (подозреваю, что и iESX) до какого-то билда имеют косяк, который состоит в том, что 64-битные счетчики трафика работают для исходящего трафика и возвращают 0 для входящего. Потом vmware это пофиксила, но если у вас вдруг при мониторинге сетевого трафика исходящий есть, а входящего нет — это оно самое, не пугайтесь, надо поднимать версию.
Системы хранения (а как легко догадаться, у кластера они внешние и могут отпадать) тоже доступны в 25 ветке (oid 1.3.6.1.2.1.25.2) за одной оговоркой — vSphere нигде и никак не возвращает имена подключенных дисков. То есть в hrDeviceDescr видим название и номер lun (LUN HP HSV300 0953 naa.50014380025cf510), а в точке монтирования что-то типа /vmfs/volumes/4e343177-a470f8bb-4e25-04257f664f9e, partiton label тоже крайне информативен (naa.600508b1001c1bc8b9036a0d8b117c88:1).
А админам даже vmware важно видеть имя диска, а не его шифр. Поэтому пришлось колхозить свой скрипт, который бы мониторил диски на vmware и описывал их в терминах, понятным окружающим.
#!/usr/local/bin/perl
#
# (C) Smithson Inc
#
#
#use strict;
use lib "/usr/local/libexec/nagios";
use utils qw($TIMEOUT %ERRORS &print_revision &support);
use vars qw($PROGNAME);
use Getopt::Long;
use Time::gmtime;
use vars qw($opt_V $opt_h $verbose $opt_w $opt_c $opt_H);
$PROGNAME = `basename $0`;
my $warning = 90;
my $critical = 95;
my $community = 'public';
my $MAX = 16;
Getopt::Long::Configure('bundling');
GetOptions
(
"w=s" => \$opt_w, "warning=s" => \$opt_w,
"c=s" => \$opt_c, "critical=s" => \$opt_c,
"H=s" => \$opt_H, "hostname=s" => \$opt_H);
$opt_H = shift unless ($opt_H);
my $host = $1 if ($opt_H =~ m/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z][-a-zA-Z0]+(\.[a-zA-Z][-a-zA-Z0]+)*)$/);
if (!(defined($host))) { print_usage(); exit $ERRORS{'UNKNOWN'}; };
my $server = $host;
($opt_c) || ($opt_c = shift) || ($opt_c = 95);
$critical = $1 if ($opt_c =~ /([0-9]+)/);
($opt_w) || ($opt_w = shift) || ($opt_w = 90);
$warning = $1 if ($opt_w =~ /([0-9]+)/);
$RETURN_CODE = $ERRORS{'UNKNOWN'};
$SNMP = "/usr/local/bin/snmpwalk -v2c -c $community";
$OID_SYSNAME = '1.3.6.1.2.1.1.5.0';
$OID_STORAGE = '1.3.6.1.2.1.25.2';
my $s = getStorageState($server);
my $code = 'UNKNOWN';
if ($RETURN_CODE == $ERRORS{'OK'}) { $code = 'OK '; }
if ($RETURN_CODE == $ERRORS{'WARNING'}) { $code = 'WARNING '; }
if ($RETURN_CODE == $ERRORS{'CRITICAL'}) { $code = 'CRITICAL '; }
print "$code - $s \n";
exit $RETURN_CODE;
# ================================================================
sub getStorageState {
my $ip = shift;
my $ret = '';
my $i;
my @size, @used, @name= ();
for ($i = 0; $i<$MAX+1; $i++) { $size[$i] = 0; }
my @s = getSNMPdata($ip, $OID_STORAGE);
if ($#s == 0) { return ''; }
$RETURN_CODE = $ERRORS{'OK'};
foreach $q (@s) {
chomp($q);
if ($q =~ /hrStorageSize\.(\d+) = INTEGER: (\d+)/) {
$size[$1] = $2;
}
if ($q =~ /hrStorageUsed\.(\d+) = INTEGER: (\d+)/) {
$used[$1] = $2;
}
if ($q =~ /hrStorageDescr\.(\d+) = STRING: \/vmfs\/volumes\/(.+)/) {
$name[$1] = getdatastoragename($2);
}
if ($q =~ /hrStorageAllocationFailures\.(\d+) = Counter32: (\d+)/) {
if ($2 != 0) {
$ret = $ret."\# $1 failure! (code $2) ";
$RETURN_CODE = $ERRORS{'CRITICAL'};
}
}
};
for ($i = 0; $i<$MAX+1; $i++) {
if ($size[$i] > 0) {
my $p = (100*$used[$i])/$size[$i];
my $sp = sprintf("%0.2f", $p);
if (length($ret) > 1) { $ret = $ret.", "; }
$ret = $ret."$name[$i]=$sp\%";
if (($p > $warning) && ($RETURN_CODE == $ERRORS{'OK'})) { $RETURN_CODE = $ERRORS{'WARNING'}; }
if ($p > $critical) { $RETURN_CODE = $ERRORS{'CRITICAL'}; }
};
}
return $ret;
}
# ================================================================
sub getSNMPdata {
my $ip = shift;
my $snmpquery = shift;
my $q, @dat;
$q = "$SNMP $ip $snmpquery";
@dat = `$q`;
return @dat;
}
# ================================================================
sub getSNMPstring {
my $ip = shift;
my $snmpquery = shift;
my $q, $dat;
$q = "$SNMP $ip $snmpquery";
$dat = `$q`;
chomp $dat;
if (length($dat) < 1) {
return '';
}
if ($dat =~ /= STRING:\ (.+)/) { $dat = $1 };
return $dat;
}
# ================================================================
sub getdatastoragename () {
my $id = shift;
my $ret = '?';
my %names = (
'4b4468fe-c310d5c8-e0ee-002481e8ae94' => 'EVA2Tb',
'4f3b9661-776069e9-5002-78e7d158f891' => 'EVA360Gb',
'54f5b0bc-97f70749-e088-f0921c1099b0' => 'NN1.5Tb',
'52e8d119-309fe1b1-10fb-d89d676e0ce0' => 'EVA2TB_2',
'55c07bc8-6365350e-c56b-3c4a92e5f7f4' => '0CLONE',
);
foreach $key (keys %names) {
if ($id eq $key) { return $names{$key}; };
};
return $ret;
}
# ================================================================
sub print_usage () {
print "Usage: $PROGNAME -H <host> [-w <warn>] [-c <crit>] \n";
}
# ================================================================
sub print_help () {
print_revision($PROGNAME,'');
print "Copyright (c) Smithson Inc, 2012\n";
print "\n";
print_usage();
print "\n";
print "<warn> = Signal strength at which a warning message will be generated.\n";
print "<crit> = Signal strength at which a critical message will be generated.\n";
support();
};
# ================================================================
Магия кроется в строчке
my %names = (
'4b4468fe-c310d5c8-e0ee-002481e8ae94' => 'EVA2Tb',
'4f3b9661-776069e9-5002-78e7d158f891' => 'EVA360Gb',
'54f5b0bc-97f70749-e088-f0921c1099b0' => 'NN1.5Tb',
'52e8d119-309fe1b1-10fb-d89d676e0ce0' => 'EVA2TB_2',
'55c07bc8-6365350e-c56b-3c4a92e5f7f4' => '0CLONE',
);
её приходится править каждый раз, когда добавляется новый диск к серверам. За четыре года — аж два раза.
Как ни загадочно это покажется, но на разных vSphere-серверах, стоящих в разных кластерах, эти коды одинаковые для одного и того же диска.
Имена и шифры локальных дисков у всех серверов разные, но в нашем случае они нам не интересны, локально у нас лежат только тестовые проекты, поэтому переполнение локального хранилища нас по большому счету не волнует. Но если вам важно — то можете сюда добавить соответствия шифров и имен для локальных datastore.
Еще специфично для vmware — это информация по виртуальным машинам. Она кроется за OID 1.3.6.1.4.1.6876
Информация по ОЗУ 1.3.6.1.4.1.6876.2.1.1.5
Информация по виртуальным машинам 1.3.6.1.4.1.6876.2.1.1.6
Информация по CPU 1.3.6.1.4.1.6876.3.1.1
Этот скрипт загоняет данные в rrdtool-базу, поскольку у нас нет необходимости реагировать на изменение нагрузки ОЗУ или число виртуальных машин. Но на его основе можно сделать и плагин для nagios.
#!/usr/local/bin/perl
#
# (C) by Smithson Inc, 2013
#
require "srv.list";
my $RRD = '/usr/local/bin/rrdtool';
my $EMPTY = ":U:U:U:U:U:U:U:U:U:U";
my @DATA = (), @VM = ();
my $OID_VMWARE = '1.3.6.1.4.1.6876';
my $OID_IF_OUT = '.1.3.6.1.2.1.31.1.1.1.6';
my $OID_IF_IN = '.1.3.6.1.2.1.31.1.1.1.10';
##my $OID_IF_IN = '.1.3.6.1.2.1.2.2.1.16';
my $SNMP = '/usr/local/bin/snmpwalk -v2c -c нескажу';
my $AWK = '/usr/bin/awk';
my $FILENAME='vmware.snmpdata';
my $MEMORY_MASK = '.6876.2.1.1.5.';
my $CORES_MASK = '.6876.2.1.1.9.';
my $MEMSIZE_MASK = '.6876.3.2.1';
my $CPUCOUNT_MASK = '.6876.3.1.1';
my $POWERED_MASK = '.6876.2.1.1.6.';
my $i;
foreach $i (sort keys %servers) {
my $traf= getIFinfo($i);
my $t = getVMinfo($i);
$t='N'.$traf.':'.$t.$EMPTY;
my $rrd = "$RRD update $DBPATH/$i.rrd $t";
system("$rrd") && print "ERROR update '$DBPATH/$i.rrd': $!\n";
}
# ================================================================
sub searchinfo { # Search info by MASK
my $mask = shift;
my $i, @ret = (), $r = 0;
my $c = @DATA+1;
for ($i = 0; $i < $c; $i++) {
if ($DATA[$i] =~ /$mask(.+)\s*=\s*([a-zA-Z0-9]+)\:\s+(.+)$/) {
$ret[$r] = $3;
$r++;
}
}
return @ret;
}
# ================================================================
sub IsVMrun { # Return 0 if VM is Off or 1 is VM is On
my $id = shift; # VM id
my $r = 0;
for ($i = 0; $i < @VM; $i++) {
if ($id == $VM[$i]) { $r = 1; last; }
}
return $r;
}
# ================================================================
sub memoryinfo { # Return 2 values - Memory Allocated All & Memory Allocated on run VMs
my $mask = $MEMORY_MASK;
my $i, $all = 0, $used = 0;
my $c = @DATA+1;
for ($i = 0; $i < $c; $i++) {
if ($DATA[$i] =~ /$mask(.+)\s*=\s*([a-zA-Z0-9]+)\:\s+(.+)$/) {
$all = $all + $3;
if (IsVMrun($1) == 1) { $used = $used + $3; }
}
}
$all = $all*1000*1000;
$used = $used*1000*1000;
return ($all, $used);
}
# ================================================================
sub coresinfo { # Return 2 values - Cores Total Allocated & Cores Allocated on run VMs
my $mask = $CORES_MASK;
my $i, $all = 0, $used = 0;
my $c = @DATA+1;
for ($i = 0; $i < $c; $i++) {
if ($DATA[$i] =~ /$mask(.+)\s*=\s*([a-zA-Z0-9]+)\:\s+(.+)$/) {
$all = $all + $3;
if (IsVMrun($1) == 1) { $used = $used + $3; }
}
}
return ($all, $used);
}
# ================================================================
sub getVMinfo {
my $ip = shift;
my $ret, $s, $count = 0;
my $q = "$SNMP $ip $OID_VMWARE >$FILENAME.$ip";
`$q`;
$#DATA = -1;
open(F, "<$FILENAME.$ip");
while (defined($s=<F> )) {
chomp $s;
push @DATA, $s;
}
close F;
unlink("$FILENAME.$ip");
($count, $oncount) = countVM();
my ($memall, $memused) = memoryinfo();
my ($corecount, $coreused) = coresinfo();
my ($memsize) = searchinfo($MEMSIZE_MASK);
$memsize = ($memsize/1024)*1000000;
my ($cpucount) = searchinfo($CPUCOUNT_MASK);
$ret = "$count:$corecount:$cpucount:$servers{$ip}:U:U:U:U:$memsize:$memused:$oncount:$memall:$coreused";
return $ret;
}
# ================================================================
sub countVM { # Return 2 values - VMcount & VMUpCount; filled the @VM array by indexs of UP VMs.
my $r = 0, $up = 0, $i, $c = @DATA+1;
@VM = ();
for ($i = 0; $i < $c; $i++) {
if ($DATA[$i] =~ /$POWERED_MASK(.+)\s*=\s*([a-zA-Z0-9]+)\:\s+\"(powered.+)\"/) {
$r++;
my $f1 = $1, $f2 = $2, $f3 = $3;
if ($f3 =~ /on/i) {
$up++;
push @VM, $f1;
}
}
}
return ($r, $up);
};
# ================================================================
sub getIFinfo {
my $ip = shift;
my $i, $c = 0;
my $q, $ret = "";
for ($i = 1; $i < 5; $i++) {
$q = $SNMP." $ip $OID_IF_IN\.$i | $AWK \'\{print \$4\}\'";
my $s = `$q`;
chomp $s;
if ($s =~ /^(\d+)$/) {
$ret=$ret.":$s";
$c++;
}
};
for ( ; $c < 4; $c++ ) {
$ret = $ret.':U';
}
for ($i = 1; $i < 5; $i++) {
$q = $SNMP." $ip $OID_IF_OUT\.$i | $AWK \'\{print \$4\}\'";
my $s = `$q`;
chomp $s;
if ($s =~ /^(\d+)$/) {
$ret=$ret.":$s";
$c++;
}
};
for ( ; $c < 8; $c++ ) {
$ret = $ret.':U';
}
return $ret;
}
%servers = (
'10.11.1.11' => 40,'10.11.1.12' => 40,'10.11.1.14' => 40,'10.11.1.15' => 40,
'10.11.1.8' => 32,'10.11.1.9' => 32,'10.11.1.7' => 32,
'10.11.1.3' => 24,'10.11.1.6' => 24
);
$targetdir = '/data//rrdtool/www/vmware';
$DBPATH = '/data/rrdtool/vmware/data';
@periodlist = ('10h', '2d', '10d', '30d', '1y', '3y');
Значения хеша — это максимальное число процессоров (ядер) на данном сервере. Мне было проще сделать так, чем вычислять это число каждый раз на основе подсчета строк ProcessorLoad или CPU Pkg/ID/Node. Число процессоров у сервера меняется ОЧЕНЬ редко :)
srv.lst — это такой общий файлик, который атачится как к скрипту мониторинга, так и к скрипту генерации html-страниц и скрипту рисования.
Теперь поговорим о Rittal CMC-TC.
Rittal CMC-TС — это разработка компании rittal для мониторинга условий среды — температуры, влажности и силы воздушного потока. Правда, датчики воздушного потока там уродские, они возвращают не скорость «ветра», а 0 — нет потока и 1 — есть поток. Причем регулируются они аналогово, штыковой отверткой крутишь вернер датчика и таким образом устанавливаешь порог срабатывания между «ветер есть» и «ветра нет». Но песня не о том.
Сама система состоит из процессорного модуля PU, к которому можно подключить до 4 (гнезда так и пронумерованы — 1, 2, 3 и 4) хабов датчиков. К каждому хабу можно подключить до 4 датчиков.
Дальше вступает в силу малопостижимая логика. Мониторинг по snmp возможен (snmp настраивается в настройках сети) и скрывается за OID .1.3.6.1.4.1.2606.4.2. Первый подключенный хаб датчиков имеет номер 3, второй — 4, третий — 5 и четвертый — 6. Все логично, не так ли? Датчики на хабе в свою очередь нумеруются 1, 2, 3 и 4, что наводит на мысль, что эти два устройства программировали разные малознакомые люди. И между номером датчика и номером хаба еще 4(!) ветки OID.
Еще раз:
.1.3.6.1.4.1.2606.4.2.3.5.2.1.5.1 — это показания первого датчика первого хаба.
.1.3.6.1.4.1.2606.4.2.6.5.2.1.5.4 — это показания четвертого датчика четвертого хаба.
И не спрашивайте меня, почему так :)
define command {
command_name check_snmp_oid
command_line $USER1$/check_snmp -H $HOSTADDRESS$ -o $ARG1$ -C $ARG2$ -w $ARG3$ -c $ARG4$ -u $ARG5$ -l ""
}
define service{
name temperature-service
use generic-service
register 0
contact_groups conditions
notification_options c,r
}
define service{
use temperature-service
host_name CMC-02
service_description Temperature Floor 12 point 1
check_command check_snmp_oid!.1.3.6.1.4.1.2606.4.2.5.5.2.1.5.3!секрет!20!31!C
}
Как вы легко теперь угадаете, это опрос третьего датчика на третьем же хабе на процессорном модуле СМС-2. И судя по «С» — это температура :)
Где и как вы расположите датчики — дело ваше, не забудьте только нарисовать карту, она пригодится.
Nagios и Synology
.У Synology есть отличные полубытовые сетевые дисковые хранилища, которые мы используем для резервного бакапа (набрать 40 Тб на synology стоит около 300 000 рублей, а на HP EVA или HP 3PAR — и трех миллионов может не хватить). Поэтому synology у нас много, дисков в них тоже не мало и за всем этим надо следить. А вы как хотели?
Внутри у Synology обычный linux (работает ssh, можно ставить пакеты, например, rrdtool или mc, работает rsync) и отвечает на snmp-запросы о 25 ветке (OID 1.3.6.1.2.1.25).
Дополнительно у него можно узнать состояние дисков и их температуру.
#!/usr/local/bin/perl
#
# (C) Smithson Inc
#
#
#use strict;
use lib "/usr/local/libexec/nagios";
use utils qw($TIMEOUT %ERRORS &print_revision &support);
use vars qw($PROGNAME);
use Getopt::Long;
use Time::gmtime;
use vars qw($opt_V $opt_h $verbose $opt_w $opt_c $opt_H $volname $opt_mode $mode);
$PROGNAME = `basename $0`;
Getopt::Long::Configure('bundling');
GetOptions
("V" => \$opt_V, "version" => \$opt_V,
"h" => \$opt_h, "help" => \$opt_h,
"m=s" => \$opt_mode, "mode=s" => \$opt_mode,
"w=s" => \$opt_w, "warning=s" => \$opt_w,
"c=s" => \$opt_c, "critical=s" => \$opt_c,
"H=s" => \$opt_H, "hostname=s" => \$opt_H);
if ($opt_V) {
print_revision($PROGNAME,''); #'
exit $ERRORS{'OK'};
}
if ($opt_h) {
print_help();
exit $ERRORS{'OK'};
}
$opt_H = shift unless ($opt_H);
my $host = $1 if ($opt_H =~ m/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z][-a-zA-Z0]+(\.[a-zA-Z][-a-zA-Z0]+)*)$/);
if (!(defined($host))) { print_help(); exit $ERRORS{'ERROR'}; };
($opt_c) || ($opt_c = shift) || ($opt_c = 4);
my $critical = $1 if ($opt_c =~ /([0-9]+)/);
($opt_w) || ($opt_w = shift) || ($opt_w = 3);
my $warning = $1 if ($opt_w =~ /([0-9]+)/);
#
# $mode == 0 - get temperature
# $mode == 1 - get disk status
#
my $mode = 0;
($opt_mode) || ($opt_mode = shift) || ($opt_mode = 'temp');
if ($opt_mode =~ /status/i) { $mode = 1; }
$code = $ERRORS{'OK'};
my $community = 'public';
my $snmpwalk = "/usr/local/bin/snmpwalk -v 2c -c $community -t 15";
my $OID_DISKSTAT = '.1.3.6.1.4.1.6574.2.1.1.5';
my $OID_DISKTEMP = '.1.3.6.1.4.1.6574.2.1.1.6';
my $n = '';
if ($mode == 0) { # get temp
$n = $OID_DISKTEMP;
}
if ($mode == 1) { # get status
$n = $OID_DISKSTAT;
}
$n = getsyn($host, $n);
print "Results: $n\n";
exit ($code);
# ================================================================
sub getsyn {
my $ip = shift, $s, $ret;
my $OID = shift;
my @D = getSNMPwalk($ip, $OID);
foreach $s (@D) {
if ($s eq 'U') { exit $ERRORS{'ERROR'}; };
if (length($ret) > 0) { $ret = $ret.", "; }
if ($s =~ /\= INTEGER\: (\d+)/) {
$ret = $ret."$1";
if ($1 >= $critical) { $code = $ERRORS{'CRITICAL'}; }
if ($code != $ERRORS{'CRITICAL'} && $1 >= $warning) { $code = $ERRORS{'WARNING'}; }
}
}
return $ret;
}
# ================================================================
sub getSNMPwalk {
my $ip = shift;
my $snmpquery = shift;
my $q, @dat;
$q = "$snmpwalk $ip $snmpquery";
@dat = `$q`;
if ($#dat < 1) {
return ('U');
}
return @dat;
}
# ================================================================
sub print_usage () {
print "Usage: $PROGNAME -H <host> [-v <volumename>] [-w <warn>] [-c <crit>] [-m <mode>]\n";
}
# ================================================================
sub print_help () {
print_revision($PROGNAME,'');
print "Copyright (c) Smithson Inc, 2011\n";
print "\n";
print_usage();
print "\n";
print "<warn> = Signal strength at which a warning message will be generated.\n";
print "<crit> = Signal strength at which a critical message will be generated.\n";
print "<mode> = temp or status. Default is temp.\n\t Temp return temperature of disks\n\t Status return disks status in RAID. Correct status is 1";\n";
};
# ================================================================
с параметром mode=temp (по умолчанию) возвращает температуру всех дисков, с параметром mode=status — состояние дисков в RAID. 1 Normal, 2 Initialized, 3 Not Initialized, 4 System Partition Failed, 5 Crashed. Соответсвенно 1 — нормально, 2 — warning, всё прочее — critical.
Для опроса synology, у которых много дисков, надо увеличивать timeout (здесь стоит 15 секунд), иначе она не успевает ответить.
Nagios и ИБП
У нас стоят бесперебойники двух производителей — APC и Chloride. Начнём с APC. Специфичная для этих ИБП информация скрывается за OID .1.3.6.1.4.1.318. Что можно извлечь? Много — ttl (сколько еще протянет ups при текущей нагрузке, если прямо сейчас отнять у него электричество), работает ups от батарей или нет, надо ли менять батареи, какая температура внутри ящика, входной-выходной вольтаж, уровень заряда.
define service{
name ups-service
active_checks_enabled 1
passive_checks_enabled 1
parallelize_check 1
obsess_over_service 1
check_freshness 0
notifications_enabled 1
event_handler_enabled 1
flap_detection_enabled 1
failure_prediction_enabled 1
process_perf_data 1
retain_status_information 1
retain_nonstatus_information 1
is_volatile 0
check_period 24x7
max_check_attempts 3
normal_check_interval 5
retry_check_interval 1
notification_options c,r
notification_interval 120
notification_period 24x7
register 0
contact_groups admins,power-admins
}
#APC Battery needs replacement
define service{
use ups-service
hostgroup_name APC-smart
service_description APC Battery needs replacement
check_command check_snmp!-o .1.3.6.1.4.1.318.1.1.1.2.2.4.0 -C XXXX -c 2
notification_period workhours
}
#APC status
define service{
use ups-service
hostgroup_name APC-smart
service_description APC status
check_command check_snmp!-o .1.3.6.1.4.1.318.1.1.1.4.1.1.0 -C XXXX -c 2
contact_groups smsgroup,power-admins
}
#APC Battery temperature
define service{
use ups-service
hostgroup_name APC-smart
service_description APC Battery temperature
check_command check_snmp!-o .1.3.6.1.4.1.318.1.1.1.2.2.2.0 -C XXXX -w 38 -c 45 -u C
contact_groups smsgroup,power-admins
notification_period workhours
}
# APC worktime 100 ticks * 60 sec
define service{
use ups-service
hostgroup_name APC-smart
service_description APC WorkTime
check_command check_snmp!-o .1.3.6.1.4.1.318.1.1.1.2.2.3.0 -C XXXX -w 120000: -c 18000: -u sec*100
contact_groups power-admins
notification_period workhours
}
ttl APC возвращает в 1/100 секунды (ну удобно ему так) и для получения минут его надо делить на 6000. Думаю, такой скрипт в одну строчку сможете сочинить и сами.
Из Chloride извлечь можно меньше. Ttl, температуру, состояние (батареи или электросеть), входной-выходной вольтаж, уровень заряда. Отвечает он на стандартный OID для UPS ( 1.3.6.1.2.1.33).
define service{
use ups-service
host_name UPS1, UPS2
service_description Chloride temperature
check_command check_snmp!-o 1.3.6.1.2.1.33.1.2.7.0 -C public -w 38 -c 45 -u C
contact_groups smsgroup,power-admins
}
define service{
use ups-service
host_name UPS1, UPS2
service_description Chloride WorkTime
check_command check_snmp!-o 1.3.6.1.2.1.33.1.2.3.0 -C public -w 23: -c 11: -u min
contact_groups smsgroup,power-admins
}
Зато время работы (ttl) он возвращает в минутах, а не в сотых секунды, как APC!
Кроме того, поскольку chloride «кластерные» ИБП (обученные работать на одну нагрузку вдвоём), то у них еще можно получать информацию по фазам входного и выходного сигнала. Толку от этого, правда, мало.
Поскольку с UPS шутки плохи, то информация о неполадках в них дублируется на sms нескольким особо везучим сотрудникам.
Принтеры
Сразу скажу, nagios у меня сами принтеры не мониторит. Мы мониторим очереди iPrint, это удобнее. Но для удобства учёта и предсказания расхода картриджей у нас висит скрипт, который рисует в rrdtool графики расхода картриджей и бумаги. Можно за определенный период понять, пользуются принтером или нет (300 листов за год — можно снимать :)), как расходуются картриджи, какие там картриджи и т.д.
#!/usr/bin/perl
#
# (C) Smithson Inc, 2008
#
require "prn.list";
$RRD = '/usr/local/bin/rrdtool';
$DBNAME = '/data/rrdtool/printers/prn-pagecount.rrd';
$AWK = '/usr/bin/awk';
@DATA = ();
for ($i=1; $i < 255; $i++) {
$DATA{$i} = 'U';
}
foreach $i (@usedip) {
$DATA{$i} = getPageCount($i);
}
$s = 'N';
for ($i=1; $i < 255; $i++) {
$s = $s.":$DATA{$i}";
}
$rrd = "$RRD update $DBNAME $s";
#print "$rrd \n";
system("$rrd") && print "ERROR update '$DBNAME': $!\n";
sub getPageCount {
my $ip = shift;
#print "$ip\n";
$q = "/usr/local/bin/snmpget -v1 -c public 192.168.0.$ip 1.3.6.1.2.1.43.10.2.1.4.1.1 | $AWK \'\{print \$4\}\'";
$dat = `$q`;
chomp $dat;
if ($dat =~ /[A-z]/) { $dat = 'U' };
if (length($dat) < 1) { $dat = 'U' };
return $dat
}
#!/usr/bin/perl
#
# (C) Smithson Inc, 2008
#
use Encode;
require "prn.list";
$RRD = '/usr/local/bin/rrdtool';
$DBNAME = '/data/rrdtool/printers/prn-toner.rrd';
$SNMP = '/usr/local/bin/snmpget -v1';
$INDEXMAX = 6;
$TONERDEBUG = '';
@TONERSTATUS = ();
@PRNNAME = ();
@TONERNAME = ();
for ($i=1; $i < 256; $i++) {
$TONERNAME[$i] = "X";
$TONERSTATUS[$i] = 'U:U:U:U:U:U';
$PRNNAME[$i] = "";
}
LoadPRNData($PRNINFOfile);
foreach $i (@usedip) {
getPRNInfo($i);
}
$t = 'N';
for ($i=1; $i < 256; $i++) {
$t = $t.":$TONERSTATUS[$i]";
}
# --------- debug !
#print "$t\n";
#print "$TONERDEBUG\n";
# --------------- !
$rrd = "$RRD update $DBNAME $t";
my $q = `$rrd`;
if (length($q) > 2) { print "ERROR update '$DBNAME': $q\n"; }
StorePRNData($PRNINFOfile);
sub getPRNInfo {
my $ip = shift;
my ($res,$r) = '';
my ($i,$k);
my ($dat) = '';
my ($tonerstatus) = '1.3.6.1.2.1.43.11.1.1.9.1';
my ($tonername) = '1.3.6.1.2.1.43.11.1.1.6.1';
my ($prnname) = '1.3.6.1.2.1.1.5.0';
$q = "$SNMP -c public 192.168.0.$ip $prnname | awk \'\{print \$4\}\'";
$dat = `$q`;
chomp $dat;
# ------------ debug ----------------
#print "$ip (prnname) = $dat \n";
if (length($dat) < 1) { return }
$PRNNAME[$ip] = $dat;
$k = 1;
for ($i = 1; $i <= $INDEXMAX; $i++) {
$q = "$SNMP -c public 192.168.0.$ip $tonerstatus.$i | awk \'\{print \$4\}\'";
$dat = `$q`;
chomp $dat;
# ------------ debug ----------------
#print "$ip (status) = $dat \n";
if (length($dat) < 1) { last }
if ($dat < 0) {
if ($dat == -3) { $dat = 100 };
if ($dat == -2) { $dat = 30 };
if ($dat == -1) { $dat = 10 };
}
if (length($res) < 1) { $res = $dat }
else { $res = $res.":$dat"; }
$k++;
$q = "$SNMP -c public 192.168.0.$ip $tonername.$i";
$dat = `$q`;
chomp $dat;
$dat =~ s/\n//g;
# ------------ debug ----------------
#print "$ip (name) = $dat \n";
if ($dat =~ /STRING: \"(.+)\"/) {
$r = $r."$1 |";
}
elsif ($dat =~ /Hex-STRING: (.+)/) {
#print "$dat\n";
my $s = getStringH($1);
$r = $r."$s |";
}
else { last };
}
Encode::from_to($r, 'utf-8', 'windows-1251');
$r =~ s/\?+//g;
for ($i = $k; $i<= $INDEXMAX; $i++) {
if (length($res) < 1) { $res = 'U' }
else { $res = $res.':U'; }
}
$TONERSTATUS[$ip] = $res;
$TONERNAME[$ip] = $r;
$TONERDEBUG = $TONERDEBUG."$ip: $res \n";
}
sub StorePRNData {
my $filename = shift;
my $i;
my $s = "";
foreach $i (@usedip) {
if ((length($PRNNAME[$i]) > 2) && (length($TONERNAME[$i]) > 2)) {
$s = $s."$i = ";
$s = $s."$PRNNAME[$i] / ";
$s = $s."$TONERNAME[$i]";
$s = $s."\n";
}
}
open (F, ">$HOME/$filename");
print F $s;
close F;
}
sub getStringH {
my $s = shift;
$s =~ s/(00)//egi;
$s =~ s/([0-9a-f][0-9a-f])/chr(hex($1))/egi;
$s =~ s/ //g;
return $s;
}
#!/usr/bin/perl
#
# (C) Smithson Inc, 2008
#
use Encode;
require "prn.list";
$RRD = '/usr/local/bin/rrdtool';
$DBNAME = '/data/rrdtool/printers/prn-toner.rrd';
$IMGPATH= '/data/rrdtool/www/printers/img';
my $BLACK = '#000000';
my $YELLOW = '#FFFF00';
my $CYAN = '#00CCFF';
my $MAGENTA= '#EE00EE';
my $FUSION = '#00CC00';
my $FUSION1= '#CC0000';
my $TICKCOLOR = '#888888';
$INDEXMAX = 6;
@TONERSTATUS = ();
@PRNNAME = ();
@TONERNAME = ();
LoadPRNData ($PRNINFOfile);
DrawToners(340, 230, "-48h");
foreach $j (@periodlist) {
DrawToners(500, 300, "-$j");
}
sub DrawBlack {
# рисует картинку для черно-белого принтера - первый параметр - картридж, второй - печка
my $ip = shift; # ip address
my $width = shift;
my $height = shift;
my $period = shift;
my ($toner, $fusion) = split (/ \|/, $TONERNAME[$ip]);
my $title = $PRNNAME[$ip];
# Если имя есть, то используется оно, иначе используется ip
# Если имя есть и картинка широкая, то к имени добавляется ещё и ip
if (length($title) < 2) { $title = "192.168.0.$ip" }
elsif ($width > 350) { $title = $title." (192.168.0.$ip)" }
my $q = "$RRD graph $IMGPATH/toner".$ip.$period.".png ";
$q = $q."--start $period --end now ";
$q = $q."--width $width --height $height ";
$q = $q."--full-size-mode ";
$q = $q."--title \"$title Info ($period)\" ";
if ((length($fusion) > 31) && ($width < 350)) {
$fusion = substr($fusion, 0, 29)."...";
}
# else { $q = $q."--lazy "; }
$q = $q."--lower-limit 0 ";
$q = $q." DEF:p01=$DBNAME:prn".$ip."toner1status:AVERAGE ";
$q = $q." CDEF:pp1=p01 ";
$q = $q." CDEF:z1=p01,0,EQ ";
if (length($fusion) > 1) {
$q = $q." DEF:p02=$DBNAME:prn".$ip."toner2status:AVERAGE ";
} else { $q = $q."CDEF:p02=p01,0,\* "; }
$q = $q." CDEF:fusion1=p02,0.01,\* ";
$q = $q." CDEF:fusion=0,fusion1,- ";
# }
$q = $q." AREA:p01$BLACK:\"$toner \" ";
$q = $q."GPRINT:pp1:LAST:\"%0.0lf\\j\" ";
$q = $q." TICK:z1$TICKCOLOR:1 ";
# if (length($fusion) > 1) {
$q = $q." LINE3:fusion$FUSION:\"$fusion\" ";
$q = $q."GPRINT:p02:LAST:\"%0.0lf\\j\"";
# }
my $dat = `$q`;
}
sub DrawColor {
# рисует картинку для цветного принтера - первый параметр - черный картридж, второй-третий-четвертый - цветные
# пятый-шестой - печка
my $ip = shift; # ip address
my $width = shift;
my $height = shift;
my $period = shift;
my ($color0, $color1, $color2, $color3) = ($BLACK, $CYAN, $MAGENTA, $YELLOW);
my ($toner0, $toner1, $toner2, $toner3, $fusion, $fusion1) = split (/ \|/, $TONERNAME[$ip]);
($color0, $color1, $color2, $color3) = SortColors(($toner0, $toner1, $toner2, $toner3));
my $title = $PRNNAME[$ip];
# Если имя есть, то используется оно, иначе используется ip
# Если имя есть и картинка широкая, то к имени добавляется ещё и ip
if (length($title) < 2) { $title = "192.168.0.$ip" }
elsif ($width > 350) { $title = $title." (192.168.0.$ip)" }
my $q = "$RRD graph $IMGPATH/toner".$ip.$period.".png ";
$q = $q."--start $period --end now ";
$q = $q."--width $width --height $height ";
$q = $q."--full-size-mode ";
$q = $q."--title \"$title Info ($period)\" ";
if ((length($fusion1) > 31) && ($width < 350)) {
$fusion1 = substr($fusion1, 0, 29)."...";
}
else { $q = $q."--lazy "; }
$q = $q."--lower-limit 0 ";
$q = $q." DEF:p01=$DBNAME:prn".$ip."toner1status:AVERAGE ";
$q = $q." DEF:p02=$DBNAME:prn".$ip."toner2status:AVERAGE ";
$q = $q." DEF:p03=$DBNAME:prn".$ip."toner3status:AVERAGE ";
$q = $q." DEF:p04=$DBNAME:prn".$ip."toner4status:AVERAGE ";
$q = $q." CDEF:pp1=p01 ";
$q = $q." CDEF:pp2=p02 ";
$q = $q." CDEF:pp3=p03 ";
$q = $q." CDEF:pp4=p04 ";
$q = $q." CDEF:z1=p01,0,EQ ";
$q = $q." CDEF:z2=p02,0,EQ ";
$q = $q." CDEF:z3=p03,0,EQ ";
$q = $q." CDEF:z4=p04,0,EQ ";
$q = $q." CDEF:z5=z1,z2,+ ";
$q = $q." CDEF:z6=z5,z3,+ ";
$q = $q." CDEF:z0=z6,z4,+ ";
$q = $q." DEF:p05=$DBNAME:prn".$ip."toner5status:AVERAGE ";
$q = $q." CDEF:fusion0=p05,0.1,\* ";
$q = $q." CDEF:fusion=0,fusion0,- ";
$q = $q." DEF:p06=$DBNAME:prn".$ip."toner6status:AVERAGE ";
$q = $q." CDEF:fusion2=p06,0.1,\* ";
$q = $q." CDEF:fusion1=0,fusion2,- ";
$q = $q." TICK:z0$TICKCOLOR:1 ";
$q = $q." AREA:pp1$color0:\"$toner0\" ";
$q = $q."GPRINT:pp1:LAST:\"%0.0lf\\j\" ";
$q = $q." STACK:pp2$color1:\"$toner1\" ";
$q = $q."GPRINT:pp2:LAST:\"%0.0lf\\j\" ";
$q = $q." STACK:pp3$color2:\"$toner2\" ";
$q = $q."GPRINT:pp3:LAST:\"%0.0lf\\j\" ";
$q = $q." STACK:pp4$color3:\"$toner3\" ";
$q = $q."GPRINT:pp4:LAST:\"%0.0lf\\j\" ";
if (length($fusion) > 1) {
$q = $q." LINE3:fusion$FUSION:\"$fusion\" ";
$q = $q."GPRINT:p05:LAST:\"%0.0lf\\j\" ";
}
if (length($fusion1) > 1) {
$q = $q." LINE3:fusion1$FUSION1:\"$fusion1\" ";
$q = $q."GPRINT:p06:LAST:\"%0.0lf\\j\" ";
}
my $dat = `$q`;
}
sub SortColors {
# принимает массив названий картриджей и возвращает список цветов согласно цветам картриджей
my @list = @_;
my (@c) = ($BLACK, $CYAN, $MAGENTA, $YELLOW);
my $i;
for ($i = 0; $i < 4; $i++) {
my $s = $list[$i];
if ($s =~ /Black/i) {
$c[$i] = $BLACK;
}
if ($s =~ /Cyan/i) {
$c[$i] = $CYAN;
}
if ($s =~ /Magent/i) {
$c[$i] = $MAGENTA;
}
if ($s =~ /Yellow/i) {
$c[$i] = $YELLOW;
}
}
return @c;
}
sub DrawToners {
# рисует весь массив принтеров, разделяя их на цветные или черно-белые
my $width = shift;
my $height = shift;
my $period = shift;
my ($i);
my ($name);
foreach $i (@usedip) {
$name = $TONERNAME[$i];
my @aa = split(/\|/, $name);
my $qa = $#aa+1;
#print "$i = $qa ($name) \n";
if ($qa > 3) {
DrawColor($i, $width, $height, $period);
}
else {
DrawBlack($i, $width, $height, $period);
}
};
}
@usedip = (
115,
122,124,125,128,129,
131,132,136,137,
140,141,142,145,147,148,
150,152,155,157,
160,162,164,165,166,167,168,169,
171,172,173,174,175,176,177,179,
180,182,183,185,186,188,189,
190,191,192,194,195,197,198,199,
201,202,203,205,207,208,209,
210,212,215,216,217,219,
220,225,
235,236,237,
241,245
);
@periodlist = ('14h', '2d', '10d', '30d', '1y');
$HOME = '/data/rrdtool/printers';
$PRNINFOfile = 'prn.info';
sub LoadPRNData {
my $filename = shift;
my $i;
my $s = "";
my @data;
open (F, "$HOME/$filename");
@data=<F>;
close F;
foreach $s (@data) {
if ($s =~ /(\d+) = (.+) \/ (.*)/) {
$i = $1;
$PRNNAME[$i] = $2;
$TONERNAME[$i] = $3;
}
}
}
Суть этой магии в том, что при рисовании данные берутся из заранее сформированного файла вида:
216 = NPI93F7F3 / Black Cartridge HP CE278A |
219 = NPID6E096 / Black Cartridge HP CE278A |
220 = HOZY / Black Print Cartridge HP Q1339A |Maintenance Kit HP 110V-Q2436A, 220V-Q2437A |
225 = NPIBAA4EA / Toner Cartridge HP C4127X |
235 = Ditat_HP4700_Color / Black Cartridge HP Q5950A |Cyan Cartridge HP Q5951A |Magenta Cartridge HP Q5953A |Yellow Cartridge HP Q5952A |Image Transfer Kit HP Q7504A |Image Fuser Kit HP 110V-Q7502A, 220V-Q7503A |
237 = NPI7C543C / BlackCartridgeHPCC364X |MaintenanceKitHP110V-CB388A,220V-CB389A |
241 = WorkCentre / Black Toner Cartridge |Yellow Toner Cartridge |Magenta Toner Cartridge |Cyan Toner Cartridge |Waste Toner Container |
245 = NPI0E8A4D / Black Print Cartridge HP C8543X |Maintenance Kit HP 110V-C9152A, 220V-C9153A |
При большом количестве принтеров это сильно снижает нагрузку как на «рисовальный» сервер, так и на сами принтеры.
И немного о Cisco
Да, мониторинг cisco через nagios стандартнее некуда. Но есть нюанс. Как всегда, при интенсивной работе routers и switchs обрастают новыми интерфейсами. Туннели, vlans и прочие подинтерфейсы возникают и накапливаются, их ставят на мониторинг и всё вроде бы отлично. Но потом случается она, перезагрузка — и вдруг выясняется, что при старте cisco отсортировала интерфейсы по типам, а внутри типов — по номерам и те номера в дереве mibs, которые вы обнаруживали и прописывали в мониторинге — уже не те.
Во избежание надо мониторить по имени интерфейса. Стандартный плагин нагиоса вроде даже умеет — но не делает этого. Не находит он интерфейс по имени. Хотя имя типа Tunnel40 — куда уж уникальнее.
#! /usr/local/bin/perl -w
use POSIX;
use strict;
use lib "/usr/local/libexec/nagios" ;
use utils qw($TIMEOUT %ERRORS &print_revision &support);
use Net::SNMP;
use Getopt::Long;
&Getopt::Long::config('bundling');
my $PROGNAME = "check_iftraf";
sub print_help ();
sub usage ($);
sub print_usage ();
sub process_arguments ();
my $timeout;
my $status;
my %ifOperStatus = ('1','up',
'2','down',
'3','testing',
'4','unknown',
'5','dormant',
'6','notPresent',
'7','lowerLayerDown'); # down due to the state of lower layer interface(s)
my $state = "UNKNOWN";
my $answer = "";
my $snmpkey = 0;
my $community = "public";
my $maxmsgsize = 1472 ; # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $privproto, $auth, $priv, $context);
my $port = 161;
my @snmpoids;
my $sysUptime = '1.3.6.1.2.1.1.3.0';
my $snmpIfDescr = '1.3.6.1.2.1.2.2.1.2';
my $snmpIfType = '1.3.6.1.2.1.2.2.1.3';
my $snmpIfAdminStatus = '1.3.6.1.2.1.2.2.1.7';
my $snmpIfOperStatus = '1.3.6.1.2.1.2.2.1.8';
my $snmpIfName = '1.3.6.1.2.1.31.1.1.1.1';
my $snmpIfLastChange = '1.3.6.1.2.1.2.2.1.9';
my $snmpIfAlias = '1.3.6.1.2.1.31.1.1.1.18';
my $snmpLocIfDescr = '1.3.6.1.4.1.9.2.2.1.1.28';
my $snmpMIBIN = '1.3.6.1.2.1.2.2.1.10';
my $snmpMIBOUT = '1.3.6.1.2.1.2.2.1.16';
my $hostname;
my $ifName;
my $session;
my $error;
my $response;
my $snmp_version = 2 ;
my $ifXTable;
my $opt_h ;
my $opt_V ;
my $ifdescr;
my $iftype;
my $key;
my $lastc;
my $dormantWarn;
my $adminWarn;
my $name;
my %session_opts;
### Validate Arguments
$status = process_arguments();
# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
print ("ERROR: U U No snmp response from $hostname (alarm)\n");
exit $ERRORS{"UNKNOWN"};
};
alarm($timeout);
($session, $error) = Net::SNMP->session(%session_opts);
if (!defined($session)) {
$state='UNKNOWN';
$answer=' U U '.$error;
print ("$state: $answer\n");
exit $ERRORS{$state};
}
## map ifdescr to ifindex - should look at being able to cache this value
if (defined $ifdescr || defined $iftype) {
# escape "/" in ifdescr - very common in the Cisco world
if (defined $iftype) {
$status=fetch_ifindex($snmpIfType, $iftype);
} else {
$ifdescr =~ s/\//\\\//g;
$status=fetch_ifindex($snmpIfDescr, $ifdescr); # if using on device with large number of interfaces
# recommend use of SNMP v2 (get-bulk)
}
if ($status==0) {
$state = "UNKNOWN";
printf "$state: U U could not retrive ifdescr/iftype snmpkey - $status-$snmpkey\n";
$session->close;
exit $ERRORS{$state};
}
}
## Main function
$snmpIfAdminStatus = $snmpIfAdminStatus . "." . $snmpkey;
$snmpIfOperStatus = $snmpIfOperStatus . "." . $snmpkey;
$snmpIfDescr = $snmpIfDescr . "." . $snmpkey;
$snmpIfName = $snmpIfName . "." . $snmpkey ;
$snmpIfAlias = $snmpIfAlias . "." . $snmpkey ;
$snmpMIBIN = $snmpMIBIN . "." . $snmpkey ;
$snmpMIBOUT = $snmpMIBOUT . "." . $snmpkey ;
push(@snmpoids,$snmpIfAdminStatus);
push(@snmpoids,$snmpIfOperStatus);
push(@snmpoids,$snmpIfDescr);
push(@snmpoids,$snmpIfName) if (defined $ifXTable) ;
push(@snmpoids,$snmpIfAlias) if (defined $ifXTable) ;
push(@snmpoids,$snmpMIBIN);
push(@snmpoids,$snmpMIBOUT);
if (!defined($response = $session->get_request(@snmpoids))) {
$answer=$session->error;
$session->close;
$state = 'WARNING';
print ("$state: SNMP error: $answer\n");
exit $ERRORS{$state};
}
$answer = sprintf("host '%s', %s(%s) is %s\n",
$hostname,
$response->{$snmpIfDescr},
$snmpkey,
$ifOperStatus{$response->{$snmpIfOperStatus}}
);
## Check to see if ifName match is requested and it matches - exit if no match
## not the interface we want to monitor
if ( defined $ifName && not ($response->{$snmpIfName} eq $ifName) ) {
$state = 'UNKNOWN';
$answer = "U U Interface name ($ifName) doesn't match snmp value ($response->{$snmpIfName}) (index $snmpkey)";
print ("$state: $answer\n");
exit $ERRORS{$state};
}
## define the interface name
if (defined $ifXTable) {
$name = $response->{$snmpIfName} ." - " .$response->{$snmpIfAlias} ;
}else{
$name = $response->{$snmpIfDescr} ;
}
## if AdminStatus is down - some one made a consious effort to change config
##
if ( not ($response->{$snmpIfAdminStatus} == 1) ) {
$answer = "Interface $name (index $snmpkey) is administratively down.";
if ( not defined $adminWarn or $adminWarn eq "w" ) {
$state = 'WARNING';
} elsif ( $adminWarn eq "i" ) {
$state = 'OK';
} elsif ( $adminWarn eq "c" ) {
$state = 'CRITICAL';
} else { # If wrong value for -a, say warning
$state = 'WARNING';
}
}
## Check operational status
elsif ( $response->{$snmpIfOperStatus} == 2 ) {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) is down.";
} elsif ( $response->{$snmpIfOperStatus} == 5 ) {
if (defined $dormantWarn ) {
if ($dormantWarn eq "w") {
$state = 'WARNING';
$answer = "U U Interface $name (index $snmpkey) is dormant.";
}elsif($dormantWarn eq "c") {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) is dormant.";
}elsif($dormantWarn eq "i") {
$state = 'OK';
$answer = "U U Interface $name (index $snmpkey) is dormant.";
}
}else{
# dormant interface - but warning/critical/ignore not requested
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) is dormant.";
}
} elsif ( $response->{$snmpIfOperStatus} == 6 ) {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) notPresent - possible hotswap in progress.";
} elsif ( $response->{$snmpIfOperStatus} == 7 ) {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) down due to lower layer being down.";
} elsif ( $response->{$snmpIfOperStatus} == 3 || $response->{$snmpIfOperStatus} == 4 ) {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) down (testing/unknown).";
} else {
$state = 'OK';
my $inQ = $response->{$snmpMIBIN};
my $outQ = $response->{$snmpMIBOUT};
$answer = "$inQ $outQ";
}
print ("$state: $answer\n");
exit $ERRORS{$state};
### subroutines
sub fetch_ifindex {
my $oid = shift;
my $lookup = shift;
if (!defined ($response = $session->get_table($oid))) {
$answer=$session->error;
$session->close;
$state = 'CRITICAL';
printf ("$state: SNMP error with snmp version $snmp_version ($answer)\n");
$session->close;
exit $ERRORS{$state};
}
foreach $key ( keys %{$response}) {
if ($response->{$key} =~ /$lookup/) {
$key =~ /.*\.(\d+)$/;
$snmpkey = $1;
#print "$lookup = $key / $snmpkey \n"; #debug
}
}
unless (defined $snmpkey) {
$session->close;
$state = 'CRITICAL';
printf "$state: Could not match $ifdescr on $hostname\n";
exit $ERRORS{$state};
}
return $snmpkey;
}
sub usage($) {
print "$_[0]\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
sub print_usage() {
printf "\n";
printf "usage: \n";
printf "check_iftraf -d <IF_NAME or IF_DESC> -H <HOSTNAME> [-C <community>]\n";
printf "Copyright (C) 2000 Christoph Kron\n";
printf "check_iftraf.pl comes with ABSOLUTELY NO WARRANTY\n";
printf "This programm is licensed under the terms of the ";
printf "GNU General Public License\n(check source code for details)\n";
printf "\n\n";
}
sub print_help() {
print_revision($PROGNAME, '');
print_usage();
printf "check_iftraf plugin for Nagios monitors operational \n";
printf "status of a particular network interface on the target host\n";
printf "\nUsage:\n";
printf " -H (--hostname) Hostname to query - (required)\n";
printf " -C (--community) SNMP read community (defaults to public,\n";
printf " used with SNMP v1 and v2c\n";
printf " -v (--snmp_version) 1 for SNMP v1 (default)\n";
printf " 2 for SNMP v2c\n";
printf " SNMP v2c will use get_bulk for less overhead\n";
printf " if monitoring with -d\n";
printf " -L (--seclevel) choice of \"noAuthNoPriv\", \"authNoPriv\", or \"authPriv\"\n";
printf " -U (--secname) username for SNMPv3 context\n";
printf " -c (--context) SNMPv3 context name (default is empty string)\n";
printf " -A (--authpass) authentication password (cleartext ascii or localized key\n";
printf " in hex with 0x prefix generated by using \"snmpkey\" utility\n";
printf " auth password and authEngineID\n";
printf " -a (--authproto) Authentication protocol (MD5 or SHA1)\n";
printf " -X (--privpass) privacy password (cleartext ascii or localized key\n";
printf " in hex with 0x prefix generated by using \"snmpkey\" utility\n";
printf " privacy password and authEngineID\n";
printf " -P (--privproto) privacy protocol (DES or AES; default: DES)\n";
printf " -k (--key) SNMP IfIndex value\n";
printf " -d (--descr) SNMP ifDescr value\n";
printf " -T (--type) SNMP ifType integer value (see http://www.iana.org/assignments/ianaiftype-mib)\n";
printf " -p (--port) SNMP port (default 161)\n";
printf " -I (--ifmib) Agent supports IFMIB ifXTable. Do not use if\n";
printf " you don't know what this is. \n";
printf " -n (--name) the value should match the returned ifName\n";
printf " (Implies the use of -I)\n";
printf " -w (--warn =i|w|c) ignore|warn|crit if the interface is dormant (default critical)\n";
printf " -D (--admin-down =i|w|c) same for administratively down interfaces (default warning)\n";
printf " -M (--maxmsgsize) Max message size - usefull only for v1 or v2c\n";
printf " -t (--timeout) seconds before the plugin times out (default=$TIMEOUT)\n";
printf " -V (--version) Plugin version\n";
printf " -h (--help) usage help \n\n";
printf " -k or -d or -T must be specified\n\n";
printf "Note: either -k or -d or -T must be specified and -d and -T are much more network \n";
printf "intensive. Use it sparingly or not at all. -n is used to match against\n";
printf "a much more descriptive ifName value in the IfXTable to verify that the\n";
printf "snmpkey has not changed to some other network interface after a reboot.\n\n";
}
sub process_arguments() {
$status = GetOptions(
"V" => \$opt_V, "version" => \$opt_V,
"h" => \$opt_h, "help" => \$opt_h,
"v=i" => \$snmp_version, "snmp_version=i" => \$snmp_version,
"C=s" => \$community, "community=s" => \$community,
"L=s" => \$seclevel, "seclevel=s" => \$seclevel,
"a=s" => \$authproto, "authproto=s" => \$authproto,
"U=s" => \$secname, "secname=s" => \$secname,
"A=s" => \$authpass, "authpass=s" => \$authpass,
"X=s" => \$privpass, "privpass=s" => \$privpass,
"P=s" => \$privproto, "privproto=s" => \$privproto,
"c=s" => \$context, "context=s" => \$context,
"k=i" => \$snmpkey, "key=i",\$snmpkey,
"d=s" => \$ifdescr, "descr=s" => \$ifdescr,
"l=s" => \$lastc, "lastchange=s" => \$lastc,
"p=i" => \$port, "port=i" =>\$port,
"H=s" => \$hostname, "hostname=s" => \$hostname,
"I" => \$ifXTable, "ifmib" => \$ifXTable,
"n=s" => \$ifName, "name=s" => \$ifName,
"w=s" => \$dormantWarn, "warn=s" => \$dormantWarn,
"D=s" => \$adminWarn, "admin-down=s" => \$adminWarn,
"M=i" => \$maxmsgsize, "maxmsgsize=i" => \$maxmsgsize,
"t=i" => \$timeout, "timeout=i" => \$timeout,
"T=i" => \$iftype, "type=i" => \$iftype,
);
if ($status == 0){
print_help();
exit $ERRORS{'OK'};
}
if ($opt_V) {
print_revision($PROGNAME,'');
exit $ERRORS{'OK'};
}
if ($opt_h) {
print_help();
exit $ERRORS{'OK'};
}
if (! utils::is_hostname($hostname)){
usage("Hostname invalid or not given");
}
unless ($snmpkey > 0 || defined $ifdescr || defined $iftype){
usage("Either a valid snmp key (-k) or a ifDescr (-d) must be provided");
}
if (defined $ifName) {
$ifXTable=1;
}
if (defined $dormantWarn) {
unless ($dormantWarn =~ /^(w|c|i)$/ ) {
printf "Dormant alerts must be one of w|c|i \n";
exit $ERRORS{'UNKNOWN'};
}
}
unless (defined $timeout) {
$timeout = $TIMEOUT;
}
if ($snmp_version !~ /[123]/){
$state='UNKNOWN';
print ("$state: No support for SNMP v$snmp_version yet\n");
exit $ERRORS{$state};
}
%session_opts = (
-hostname => $hostname,
-port => $port,
-version => $snmp_version,
-maxmsgsize => $maxmsgsize
);
$session_opts{'-community'} = $community if (defined $community && $snmp_version =~ /[12]/);
if ($snmp_version =~ /3/ ) {
# Must define a security level even though default is noAuthNoPriv
# v3 requires a security username
if (defined $seclevel && defined $secname) {
$session_opts{'-username'} = $secname;
# Must define a security level even though defualt is noAuthNoPriv
unless ( grep /^$seclevel$/, qw(noAuthNoPriv authNoPriv authPriv) ) {
usage("Must define a valid security level even though default is noAuthNoPriv");
}
# Authentication wanted
if ( $seclevel eq 'authNoPriv' || $seclevel eq 'authPriv' ) {
if (defined $authproto && $authproto ne 'MD5' && $authproto ne 'SHA1') {
usage("Auth protocol can be either MD5 or SHA1");
}
$session_opts{'-authprotocol'} = $authproto if(defined $authproto);
if ( !defined $authpass) {
usage("Auth password/key is not defined");
}else{
if ($authpass =~ /^0x/ ) {
$session_opts{'-authkey'} = $authpass ;
}else{
$session_opts{'-authpassword'} = $authpass ;
}
}
}
# Privacy (DES encryption) wanted
if ($seclevel eq 'authPriv' ) {
if (! defined $privpass) {
usage("Privacy passphrase/key is not defined");
}else{
if ($privpass =~ /^0x/){
$session_opts{'-privkey'} = $privpass;
}else{
$session_opts{'-privpassword'} = $privpass;
}
}
$session_opts{'-privprotocol'} = $privproto if(defined $privproto);
}
# Context name defined or default
unless ( defined $context) {
$context = "";
}
}else {
usage("Security level or name is not defined");
}
} # end snmpv3
}
## End validation
Скрипт не мой, но я не помню, что я тут менял, чтобы оно заработало с именем интерфейса. Поэтому вот он целиком.
Ну и на этом пока всё про nagios и его плагины.
Комментарии (9)
deimond
18.08.2016 06:10+1Для cisco в зависимости от версии ios «snmp-server ifindex persist» или «snmp ifmib ifindex persist» предотвратит смену ifIndex при каждом ребуте.
Smithson
18.08.2016 09:50Спасибо, не знал. Но согласитесь, что перезагрузка cisco событие не каждого года и даже не каждой пятилетки %) Поэтому редко кто задумывается, что там будет, после потопа.
deimond
18.08.2016 10:27+1Но скрипт в over тысячу строк-то вы запилили, чтоб решить проблему сброса индексов при перезагрузке, которая случается «даже не каждую пятилетку» :) Кстати если появится необходимость мониторить QoS, пригодится еще одна команда из той же серии: «snmp mib persist cbqos».
Smithson
18.08.2016 09:54Забыл указать про vmware. Начиная с какого-то опять же билда vmware перестала отдавать по snmp загрузку процессоров. То есть mib такой есть, на запрос отвечает, но данные там лажовые и постоянные.
Did not figure out how to use SNMP, but I got vSphere Perl SDK downloaded form vmware, and used CLI apps to access vCenter API to get CPU load numbers that match those reported in vCenter.
Speccyfan
21.08.2016 20:54+1Мониторить VMware по SNMP, не самая лучшая идея, у нее есть вполне юзабельнное SDK, его использует vpoller например, довольно мощная штука. Я его использую для мониторинга свободного места на датасторах и нагрузку на хосты.
Smithson
22.08.2016 15:02Из моего опыта, vmware snmp-ответы кеширует. Если часто опрашивать хост (в режиме отладки, например), то видно, что значения меняются с шагом 2-3 минуты, не быстрее. В пределах этих 2-3 минут возвращается один и тот же результат.
То есть нагрузка от snmp на гипервизор ограничена таким способом.
На vpoller посмотрю, спасибо.
AcidVenom
Ветка Synology — .1.3.6.1.4.1.6574. И есть Гайд, а в нем ссылка на MIB'ы.
P.S.: Вы как раз ее и используете.
Smithson
Вы правы, забыл указать: OID Synolog 1.3.6.1.4.1.6574. Статью править уже не буду, пусть будет тут.