Nagios — моніторинг vmware, CMC-TC, Synology, ДБЖ, принтерів і зовсім небагато Cisco

Перша частина була тут: Nagios — система моніторингу і деякі саморобні плагіни. Як і обіцяв, частина друга, цікавіше.

Тут я розповім, як і що можна в 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 і описував їх у термінах, зрозумілих оточуючим.

check_vmwarediskstatus.pl
#!/usr/local/bin/perl
#
# © 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.2 f", $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 © 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.

check_vmwaregetstatus.pl
#!/usr/local/bin/perl
#
# © 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;
}


srv.lst

%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-ТС — це розробка компанії 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).
Додатково у нього можна дізнатися стан дисків і їх температуру.

get-synologydisks.pl
#!/usr/local/bin/perl
#
# © 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 -2c v -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 © 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 від батарей або ні, треба міняти батареї, яка температура всередині ящика, вхідний-вихідний вольтаж, рівень заряду.

моніторинг APC

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).

nagios+chloride

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 аркушів за рік — можна знімати :)), як витрачаються картриджі, які там картриджі і т. д.

printers_get_snmp.pl
#!/usr/bin/perl
#
# © 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
}


toners_get_snmp.pl
#!/usr/bin/perl
#
# © 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;
}


toners_draw.pl
#!/usr/bin/perl
#
# © 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.0 lf\\j\" ";
$q = $q." TICK:z1$TICKCOLOR:1 ";
# if (length($fusion) > 1) {
$q = $q." LINE3:fusion$FUSION:\"$fusion\" ";
$q = $q."GPRINT:p02:LAST:\"%0.0 lf\\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.0 lf\\j\" ";
$q = $q." STACK:pp2$color1:\"$toner1\" ";
$q = $q."GPRINT:pp2:LAST:\"%0.0 lf\\j\" ";
$q = $q." STACK:pp3$color2:\"$toner2\" ";
$q = $q."GPRINT:pp3:LAST:\"%0.0 lf\\j\" ";
$q = $q." STACK:pp4$color3:\"$toner3\" ";
$q = $q."GPRINT:pp4:LAST:\"%0.0 lf\\j\" ";
if (length($fusion) > 1) {
$q = $q." LINE3:fusion$FUSION:\"$fusion\" ";
$q = $q."GPRINT:p05:LAST:\"%0.0 lf\\j\" ";
}

if (length($fusion1) > 1) {
$q = $q." LINE3:fusion1$FUSION1:\"$fusion1\" ";
$q = $q."GPRINT:p06:LAST:\"%0.0 lf\\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);
}
};
}


prn.list

@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;
}
}
}


Суть цієї магії в тому, що при малюванні дані беруться із заздалегідь сформованого файлу виду:

prn.info

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 — куди вже більш унікальним.

check_iftraf
#! /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 interface layer(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 "і" ) {
$state = 'OK';
} elsif ( $adminWarn eq "с" ) {
$state = 'CRITICAL';
} else { # If wrong value for -a, say warning
$state = 'WARNING';
}
} 
## Check status operational
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 "с") {
$state = 'CRITICAL';
$answer = "U U Interface $name (index $snmpkey) is dormant.";
}elsif($dormantWarn eq "і") {
$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};


### підпрограм

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 © 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 and password 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 " І (--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 " -or k -d or -T must be specified\n\n";
printf "Note: either -or k -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,
"І" => \$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/ ) {
# Define a Must security level even though default is noAuthNoPriv
# v3 requires a security username
if (defined $seclevel && defined $secname) {
$session_opts{'-username'} = $secname;

# Define a Must 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 і його плагіни.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.