Pull to refresh

Nagios — система мониторинга и некоторые самодельные плагины

Reading time 22 min
Views 70K
Когда я выбирал системы мониторинга, я сравнивал кактус, нагиос и забикс. И выбрал нагиос. Сейчас моей истории мониторинга уже десяток лет, накопилось самописных решений, аналоги части из которых можно найти в интернете, а части и нет. Поэтому решил собрать всё в одно место, пусть лежит. Если вы сами плотно много лет используете нагиос, то вряд ли найдёте здесь какие-нибудь откровения, но в копилку может что и пригодится. А для начинающих может оказаться полезно.

Итак, поехали. Пара говорок — я описываю мониторинг со стороны nagios, поэтому как и что настраивать на серверах, которые надо мониторить — упоминаю в общем виде. И второе — я не люблю имена mibs, стараюсь использовать oid, циферки. Если по ним искать в гугле, то вы найдёте и их имена, и соседние мибы. Собственно, знать нужный oid — это 2/3 дела в случае snmp-мониторинга.

В качестве языка программирования в данном случае я предпочитаю perl — его проще отлаживать и переносить между платформами.

Я обязательно буду приводить примеры плагинов (иначе для чего бы я эту статью затеял), в том числе может быть стандартных, потому что уже не помню, что шло в комплекте, что искалось с миру по нитке и допиливалось. Помню лишь, что писал сам с нуля, там мои копирайты стоят.

Для самостоятельного осмотра дерева oid рекомендую стандартную утилиту snmpwalk. Сам нагиос у меня а) версии 3.x, б) установлен на FreeBSD, поэтому пути часто будут типичные для Free и нетипичные для linux.

Мониторинг windows-серверов


Используем банальный встроенный snmp (который есть в windows-серверах начиная с windows 2000). Сервис этот по-умолчанию не стоит, его надо добавить, настроить community name (snmp пароль) и ip адреса, с которых можно обращаться к сервису (по-умолчанию пароль public и разрешен только локальный ip). Описание windows mibs можно легко найти в инете.

Стандартный плагин check_disk_snmp.pl позволяет мониторить диски по имени (что важно, потому что порядок дисков в дереве snmp может менять после перезагрузки; если мы говорим именно о сервере, который перезагружают 1-2 раза в год; за это время у него может нарасти слой «внешних» — fibrechannel или iscsi — дисков. Буквы у них сохраняться после перезагрузки, а вот порядок в дереве snmp — не факт). А так же он позволяет мониторить состояние ОЗУ — свободно, занято, swap.

Стандартный плагин check_snmp_load.pl позволяет мониторить нагрузку cpu на сервере, а стандартные же плагины check_tcp и check_udp — доступность сетевых портов. Ибо для чего еще нужен сервер, как не для обслуживания сетевых запросов!

Описание стандартных oid, на которые отзывается windows доступно здесь. Там есть и CPU, и ОЗУ, и устройства хранения данных (в том числе по типу — CDROM, Floppy, HDD), запущенные процессы и установленные программы.

Мониторинг unix-серверов


Тут тоже всё просто. Устанавливаете на сервере пакет net-snmp, настраиваете snmpd.conf. В последних версиях там ад и хаос, я предпочитаю (вот такой я консерватор)

старую добрую схему snmp v2
rocommunity public 127.0.0.1
rocommunity vasik 10.0.9.1
без всякого новомодного ужаса, но это на любителя. Перезапускаете snmpd и извольте мониторить.

Вышепомянутый check_disk_snmp.pl умеет мониторить и unix-сервера. Плюс есть альтернатива — плагин check_snmp_storage.pl. У меня исторически сложилось так, что windows-сервера мониторятся через check_disk_snmp.pl, а unix-сервера — через check_snmp_storage.pl. Он использует ту же 25 ветку oid и тоже позволяет мониторить дисковые разделы по имени (точке монтирования). Потому что всем, кроме админа самого сервера, совершенно не интересно, что именно у него прицеплено в точку /data, или /var, или /opt, или /mnt/disk0101019084. Важно — сколько там места всего, сколько занято, сколько свободно.
Вышеупомянутый же check_snmp_load.pl умеет мониторить cpu на unix-серверах, check_tcp и check_udp — доступность сетевых портов.

Кроме того, у нагиоса есть полезный плагин check_by_ssh. Суть его в том, что он устанавливает ssh-соединение с хостом и запускает там заданную программу. Программа должна отвечать в формате nagios (код завершения 0 — успешно, 1 — warning, 2 — critical, 3 — unknown) и может выполнять любые угодные вам (и админу того сервера) проверки.

На почтовых серверах бывает полезно мониторить состояние почтовой очереди. Для этого можно использовать check_by_ssh, но так исторически сложилось, что я использую расширение snmp (напоминаю, у меня старая, обросшая ракушками система мониторинга — но это и хорошо, можно на живых примерах показать разные способы получить один и тот же результат). Плюс подхода «без ssh» очевиден — сервер мониторинга не имеет возможности подключиться к исследуемому серверу по ssh и не создает дырку в безопасности.

Итак, расширяем snmp. На исследуемом сервер в snmpd.conf пишем строчку вида extend mailq /root/getmailq.sh, где extend — команда, mailq — название ветки,

/root/getmailq.sh - выполняемая команда.
ls -al /var/spool/mqueue | wc -l

(эти конкретные сервера — freebsd, у linux или других unix расположение очереди может отличаться).

На сервере мониторинга

пишем скрипт (для разнообразия - bash)
#!/usr/bin/bash

# (C) by Smithson Inc, 2006

SNMP=/usr/bin/snmpget
HOST=$1
PASS=$2
WARN=$3
CRIT=$4

if [ -z $HOST ]; then
  echo Usage $0 HOST [SNMPCOMMUNITY [WARN [CRIT]]]
  exit 3
fi

if [ -z $PASS ]; then
  PASS=public
fi

if [ -z $WARN ]; then
  WARN=1000
fi

if [ -z $CRIT ]; then
  CRIT=5000
fi

Q=`$SNMP  -v 2c -c $PASS $HOST NET-SNMP-EXTEND-MIB::nsExtendOutLine.\"mailq\".1 | awk '{ print $4}'`


if [ $Q -ge $CRIT ]; then
  echo "CRITICAL: mailq is $Q"
  exit 2
fi

if [ $Q -ge $WARN ]; then
  echo "WARNING: mailq is $Q"
  exit 1
fi


Собственно магия кроется в запросе NET-SNMP-EXTEND-MIB::nsExtendOutLine.\«mailq\».1 — это мы читаем, что нам отдает скрипт с наблюдаемого сервера.

Ну и стандартное для нагиоса описание команды

define command{
        command_name    check-snmp-mailq
        command_line       /path-to-scripts/snmp_extend_mailq.sh $HOSTADDRESS$ $ARG1$ $ARG2$ $ARG3$
        }

Это хорошая альтернатива check_by_ssh с одной оговоркой. snmpd на сервере по-умолчанию работает от рута. А check_by_ssh может работать от другого юзера, с меньшими полномочиями. Решать вам.

Теперь о dns. Очень часто мониторинг dns сводят к банальному check_udp!53. Это очень блаародно, но малоинформативно. Сервер может работать, но не разрешать имена. Сервер может работать и быть корневым для ваших имен, но регистрация вашего домена могла протухнуть. Ничего этого вы из проверки доступности порта не увидите. Поэтому пара скриптов проверки ДНС.

Первый скрипт позволяет мониторить ваш(и) домен(ы) и не проспать необходимость их продления. Даже если домен вам продлевает провайдер автоматом, проконтролировать этот факт всегда полезно.

check-domaintime.pl
#!/usr/bin/perl
#
# (C) Smithson Inc, 2013
#
use HTTP::Date;

my $domain = $ARGV[0];
if (!(defined($domain))) { 
    print "Usage: $0 domain.name\n\n";
    exit(-1);
} 

$DD=`whois $domain | grep paid-till`;
$DAY = 86400;
$WARNING  = $ARGV[1] ? $ARGV[1] : 36;
$CRITICAL = $ARGV[2] ? $ARGV[2] : 10;

if ($DD =~ /(\d\d\d\d\.\d\d\.\d\d)/) {
    my $dx = str2timestamp($1);
    my $dz = time();
    
    print "Whois $domain end date: $1\n";
    if ($dz > ($dx-($CRITICAL*$DAY))) { exit(2); }     
    if ($dz > ($dx-($WARNING*$DAY)))  { exit(1); } 
    exit(0);
} else {
    print "Error whois answer: $DD\n";
    exit(-1);
}


sub str2timestamp {
    my $time = shift;
    $time =~ s/(\d+)\.(\d+)\.(\d+)/$1-$2-$3/;
    my $timenix = str2time( $time );
    return $timenix;
}


Используется он банально

./check-domaintime.pl smithson.ru [warning [critical]]

По-умолчанию 36 дней до окончания срока регистрации — warning, 10 дней — critical.

Второй скрипт служит для проверки работоспособности dns-сервера

check_dns.pl
#!/usr/local/bin/perl
#
# (C) Smithson Inc, 2015
#

#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,
         "a=s" => \$opt_addr, "addr=s" => \$opt_addr,
         "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_usage();  exit $ERRORS{'ERROR'}; };

my $addr = 0;
($opt_addr) || ($opt_addr = shift) || ($opt_addr = 'www.slavneft.ru');

$addr = $opt_addr;

$code = $ERRORS{'OK'};

my $look = '/usr/local/bin/nslookup';
my $n = getdns($host, $addr);
print "$n\n";
   
exit ($code);

#  ================================================================
sub getdns {
  my $ip = shift, $s, $ret='';
  my $addr = shift;
  $s = "$look $addr $ip";

  my $s = `$s`;
  
  my @D = split(/\n/, $s);
  foreach my $i (@D) {
	if ($i =~ /server can.t find/) { $code = $ERRORS{'WARNING'}; }
  	if ($i =~ /no servers could be reached/) { $code = $ERRORS{'ERROR'}; }
	$ret = $ret.' '.$i; 
  }
  
  return $ret;
}
#  ================================================================
sub print_usage () {
        print "Usage: $PROGNAME -H <host> [-a] address\n";
}
#  ================================================================
sub print_help () {
        print_revision($PROGNAME,'');
        print "Copyright (c) Smithson Inc, 2015 \n";
        print "\n";
        print_usage();
        print "\n";
        print "<address> Address for conversion to ip        \n\n";
        support();
};
#  ================================================================

Этот скрипт можно использовать двумя способами. Первый — проверка через ваш сервер доступность нужных вам адресов и вообще работы dns. Например, так:

check-dns.pl ip-вашего-сервера google.com

Если ваш сервер резолвит google.com — то dns на нём работает. В случае, если нет доступа в инет, резолвинг не сработает, но это вы увидите по другим проверкам (пинги gateway провайдера, пинг того же 8.8.8.8).

Так же вы можете проверять, что резолвятся нужные вам внутренние имена (например, AD становится плохо, если её собственный dns не распознает имена).

Второй способ — проверка через гарантированно работающий dns ваших имен, которые должны быть доступны из интернета.

check-dns.pl 8.8.8.8 smithson.ru

Если ответ есть — ваши имена из инета доступны (с оговоркой о доступности Интернет в данный момент для вашей системы мониторинга).

Мониторинг Netware


Да-да, я знаю, некрофилия фу, но даже сегодня у меня из примерно 200 серверов в системе есть 2 (два!) Netware. Оба в далеких ДО, настроенные в одна тыща восемьсот затертом году и с тех пор работают, работают и работают. У одного из uptime сегодня 834 дня. Это к слову. Поэтому — мониторинг.

На последней netware 6.5.8 есть snmp. Честно скажу — не знаю, не ел. С версии 4.11 для netware есть программка mrtgext.nlm, которая позволяет мониторить кучу параметров сервера. Вот её-то обычно и используют и для отрисовки статистики сервера через mrtg или rrdtool, ну и для мониторинга через нагиос она вполне годится. К тому же один из этих двух моих NW имеет версию 5.1 (гусары, молчать!).

mrtgext слушает tcp-порт 9999, поэтому не забудьте поставить его на мониторинг. Поскольку Netware — это прежде всего файловый сервер, то нам интереснее всего, что происходит с томами. Для это есть скриптик:

check_nwvolsize
#! /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 vars qw($opt_V $opt_h $verbose $opt_w $opt_c $opt_H $volname $opt_prefix $prefix);

$PROGNAME = `basename $0`;

Getopt::Long::Configure('bundling');
GetOptions
        ("V"   => \$opt_V, "version"    => \$opt_V,
         "h"   => \$opt_h, "help"       => \$opt_h,
         "v=s" => \$volname, "volname"  => \$volname,
         "w=s" => \$opt_w, "warning=s"  => \$opt_w,
         "c=s" => \$opt_c, "critical=s" => \$opt_c,
         "p=s" => \$opt_prefix, "prefix=s" => \$opt_prefix,
         "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_usage();  exit $ERRORS{'ERROR'}; };

($opt_c) || ($opt_c = shift) || ($opt_c = 92);
my $critical = $1 if ($opt_c =~ /([0-9]+)/);

($opt_w) || ($opt_w = shift) || ($opt_w = 80);
my $warning = $1 if ($opt_w =~ /([0-9]+)/);

($volname) || ($volname = shift) || ($volname = 'SYS');
my $vname = $volname;

my $ppp   = $opt_prefix;

my $q   = "/usr/local/sbin/nwstat.pl $host V".$ppp."U$vname V".$ppp."S$vname";
#print "$host, $vname, $critical, $warning ($ppp = $opt_prefix) \n";
#print "$q \n";
my $res = `$q`;
my @aar=split(/\n/, $res);

my $size = $aar[1];
my $used = $aar[0];

if (($size < 1) || ($used < 0)) { exit $ERRORS{'ERROR'}; }
#print "Size: $size, used: $used\n";

my $percent = ($used/$size)*100;
if ($percent > 100) { exit $ERRORS{'ERROR'}; }
printf "Used: %.2f\%\n", $percent;

if ($percent > $critical) { exit $ERRORS{'CRITICAL'}; } # Critical!
if ($percent >  $warning) { exit $ERRORS{'WARNING'};  } #  Warning!
exit $ERRORS{'OK'};   #  Okay


sub print_usage () {
        print "Usage: $PROGNAME -H <host> [-v <volumename>] [-w <warn>] [-c <crit>] [-p <prefix>]\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 "<prefix> = Special signal for nwstat, calculated size, free and used as KBytes (value K) or Bytes (value nothing).\n\n";
        support();
}


Он, в свою очередь, использует скрипт nwstat.pl, когда-то шедщий в комплекте mrtgext. Идет ли он сейчас — не знаю, поэтому выложу его сюда.

nwstat.pl
#!/usr/local/bin/perl

#########################################################
#        Netware Server Stat Extension to MRTG          #
#                  Client Access                        #
#                                                       #
# This is the "client" portion of the Netware Server    #
# stats extension for MRTG. This will open up a         #
# connection to the specified server and get the        #
# information that you specify.                         #
#                                                       #
#########################################################
# Written by James Drews (drews@engr.wisc.edu) on       #
#                Version 1.46                           #
# URL:                                                  #
#http://forge.novell.com/modules/xfmod/project/?mrtgext #
#########################################################
#                                                       #
# Feel free to contact me with any questions/comments/  #
# and such.                                             #
#                                                       #
#########################################################
# This program is freeware.  *NO* warranty is expressed,#
# implied, nor granted to you in any way.  Use of this  #
# program is at your own risk.  Your mileage may vary.  #
# This program was packed by weight, not by volume.     #
# Some settling may have occurred during shipment.      #
#########################################################

#########################################################
# Command Line Usage                                    #
#   nwstat.pl host option1 option2                      #
#  where host is the DNS name of the server to query    #
#  and option1 and option2 are any combination of the   #
#  following (case is not important):                   #
#    UTIL1      :  1 minute average CPU utilization     #
#    UTIL5      :  5 minute average CPU utilization     #
#    UTIL15     : 15 minute average CPU Utilization     #
#    LICENSE    : Connection License Count              #
#    CONNECT    : number currently licensed connections #
#    CONNMAX    : Max number licensed connections used  #
#    CONNPEAK   : Peak Connections                      #
#               : (netware 3 = error (-1) )             #
#               : (netware 4 = number connections       #
#               :  allocated)                           #
#    NAME       : Server's name                         #
#    UPTIME     : Time that the server is operational   #
#    VS<vol>    : size of the volume <vol> in bytes     #
#    VF<vol>    : bytes free on <vol>                   #
#    VU<vol>    : bytes used on <vol>                   #
#    VKS<vol>   : size of the volume <vol> in kbytes    #
#    VKF<vol>   : kbytes free on <vol>                  #
#    VKU<vol>   : kbytes used on <vol>                  #
#    VP<vol>    : bytes of purgable files on <vol>      #
#    VKP<vol>   : kbytes of purgable files on <vol>     #
#    VNP<vol>   : bytes of not-yet-purgable files       #
#    VKNP<vol>  : kbytes of not-yet-purgable files      #
#    ZERO       : Returns the value 0                   #
#    VOLUMES    : Returns the list of mounted volumes   #
#               : each volume name is on a seperate     #
#               : line. Used by the nlmcfg.pl script    #
#    S1         : Long Term Cache Hit Percentage        #
#    S2         : Current number cache buffers          #
#    S3         : Number of dirty cache buffers         #
#    S4         : Cache LRU in seconds                  #
#    S5         : Cache LRU in minutes                  #
#    S6         : Dirty cache buffers as percent of tot #
#    S7         : Total cache buffers as percent of     #
#                 original                              #
#    S8         : Original number of cache buffers      #
#    S9         : SAP object Count                      #
#    S9.x       : SAP Object count for service x        #
#    S10        : CPU Count                             #
#    S11        : IS DS Database Open? 1=yes 0=no       #
#    S12        : Logins enabled? 1=yes 0=no            #
#    S13        : DS.NLM Version string                 #
#    S14        : MRTGEXT.NLM Version string            #
#    S15        : Packet receive buffer count           #
#    S16        : Get Maximum packet receive buffer cnt #
#    S17        : Abended thread count (5.x only)       #
#    S18        : Open file count                       #
#    S19        : OS Version String                     #
#    S20        : Max service processes                 #
#    S21        : Current service processes (5.x only)  #
#    S22        : Time In Sync To the Network (0=No,    #
#                 1 = yes)                              #
#    S23:<nlm>  : Is <nlm> loaded? (0=no,1=yes)         #
#    S24:<nlm>  : Get <nlm>'s version                   #
#    S25        : Minimum Directory Cache Buffers       #
#    S26        : Maximum Directory Cache Buffers       #
#    S27        : Current Directory Cache Buffers       #
#                                                       #
# Example: To get the server utilization for 5 and 15   #
#          minutes on the myserv.mydomain.com.          #
#                                                       #
#   nwstat.pl myserv.mydomain.com UTIL5 UTIL15          #
#                                                       #
# Example: To graph the disk space usage on the SYS     #
#          volume on myserv.mydomain.com.               #
#                                                       #
#   nwstat.pl myserv.mydomain.com VFsys VUsys           #
#########################################################
#                                                       #
# Other notes:                                          #
#   The server side NLM can take ALL the options on the #
#   command line at once. However, MRTG is written to   #
#   only graph two variables at a time. Should some     #
#   ambitious person modify the program to graph more   #
#   than two items at once, this program can easily be  #
#   expanded to output more items.                      #
#                                                       #
#   The server will stop accepting input at 1023 chars  #
#   or when it gets the first \n character              #
#                                                       #
# Thanks to Kevin Keyser <kevin-keyser@uiowa.edu>       #
# for fixing the problem of loosing the 'W' char from   #
# the server name                                       #
#########################################################

# Required for perl5.
use Socket;
($_,  $opt1, $opt2) = @ARGV;

if (!$_) {
  print "Usage:  $0 HOST OPTION1 OPTION2 \n";
  print "  where host is the DNS name of the server to query\n";
  print "  and option1 and option2 are any combination of the\n";
  print "  following (case is not important):\n";
  print "  UTIL1      :  1 minute average CPU utilization\n";
  print "  UTIL5      :  5 minute average CPU utilization\n";
  print "  UTIL15     : 15 minute average CPU Utilization\n";
  print "  LICENSE    : Connection License count\n";
  print "  CONNECT    : number currently licensed connections\n";
  print "  CONNMAX    : max licensed connections used\n";
  print "  CONNPEAK   : Peak Connections\n";
  print "             : (netware 3 = error (-1) )\n";
  print "             : (netware 4 = number connections\n";
  print "             :  allocated)\n";   
  print "  VF<vol>    : bytes free on <vol>\n";
  print "  VS<vol>    : size in bytes of <vol>\n";
  print "  VU<vol>    : bytes used on <vol>\n";
  print "  VKF<vol>   : kbytes free on <vol>\n";
  print "  VKS<vol>   : size in kbytes of <vol>\n";
  print "  VKU<vol>   : kbytes used on <vol>\n";
  print "  ZERO       : returns a zero (0)\n";
  print "  S1         : Long Term Cache Hit Percentage\n";
  print "  S2         : Number of Cache Buffers\n";
  print "  S3         : Number of Dirty Cache Buffers\n";
  print "  S4         : Cache LRU in seconds\n";
  print "  S5         : Cahce LRU in minutes\n";
  print "\n Example: To graph the disk space usage on the SYS\n";
  print "          volume on myserv.mydomain.com.\n\n";
  die " $0 myserv.mydomain.com VFsys VUsys\n";
}

if (!$opt2) {
 printf "No second option specified. MRTG expects two values\n";
 die "Use \"ZERO\" for the second option if you only wnat one value.\n";
}

$hostname = $_;

# if you load the NLM with a different port
# from the default, here is where you change it
$port = 9999;

# Open a socket and get the data
  ($sockaddr,$there,$response,$tries) = ("Snc4x8");
# On Win95, passing a numeric IP address to inet_aton() is slow, so
# detect this case and use a simple conversion.  
  chomp ($hostname);
  if ($hostname =~  /^(\d+)\.(\d+)\.(\d+)\.(\d+)(.*)/ )
  {
#     $remote_addr = pack('C4',"$1.$2.$3.$4");
     $remote_addr = "$1.$2.$3.$4";
  } else {
     $remote_addr = (gethostbyname($hostname))[4]
       || die (host_not_found_error ($hostname, $?));
  }
  
#  my $addr_in = 'S n a4 x8';
#  $there = pack($addr_in, AF_INET, $port, $remote_addr);
# Конвертирует имя сервера в бинарную последовательность.
  my $iaddr = inet_aton($remote_addr);
# Упаковывает все в понятную функции connect последовательность.
  $there = sockaddr_in($port, $iaddr);
  $proto = (getprotobyname ('tcp'))[2];

  if (!socket(S,AF_INET,SOCK_STREAM,$proto)) { printf "-1\n-1\n\n\n"; die "$0:  Fatal Error.  $!\n"; }
  if (!connect(S,$there)) { printf "-2\n-2\n\n\n"; die "$0:  Fatal Error.  $!\n"; }
  select(S);$|=1;
  select(STDOUT);
  print S "$opt1 $opt2 uptime name\r\n";
  $in = int(<S>);
  print "$in\n";
  while($line = <S>) {
      print "$line";
  }
  close(S);


Использовать его легко и просто:

define command{
        command_name    check_nwvolsize
        command_line    $USER1$/check_nwvolsize -H $HOSTADDRESS$ -w $ARG2$ -c $ARG3$ -v $ARG1$ -p K
        }

Для рисования статистики:

Netware через mrtg даю шаблон

HtmlDir: /data/www/admin/mrtg
ImageDir: /data/www/admin/mrtg/images
LogDir: /data/www/admin/mrtg/logs
IconDir: /mrtg/icons
Language: Russian
Options[_]: gauge, noarrow, nopercent, unknaszero
kilo[_]: 1000


PageTop[^]: <b><<<<a href=index_nw6.html>To Index Page</b></a><br><br>
	    <table width=100% cellspacing=0 cellpadding=5 border=1 bgcolor=#dedede align=center>
	    <tr><td><center><H4>
PageTop[$]: </td></tr></table>
# ------------------- Common Parameters ------------------

Target[nw6_cpu]: `/usr/local/sbin/nwstat.pl 192.168.2.4 util5 util15`
ShortLegend[nw6_cpu]: %
YLegend[nw6_cpu]: CPU Util (%)
LegendI[nw6_cpu]: 5 minute average CPU utilization
LegendO[nw6_cpu]: 15 minute average CPU utilization
MaxBytes[nw6_cpu]: 100
Title[nw6_cpu]: CPU Utilization Analysis for Server NW6
PageTop[nw6_cpu]: CPU Utilization Analysis for Server NW6 </H4></center>

Target[nw6_conn]: `/usr/local/sbin/nwstat.pl 192.168.2.4 connect s18`
MaxBytes[nw6_conn]: 140000
ShortLegend[nw6_conn]:  
YLegend[nw6_conn]: Connections & Open files
LegendI[nw6_conn]: Number currently licenzed connections
LegendO[nw6_conn]: Open files
Title[nw6_conn]: Connection & Open Files for Server NW6
PageTop[nw6_conn]: Connection & Open Files for Server NW6 </H4></center>

# s8 = Original number of cache buffers
# s2 = Current number cache buffers
Target[nw6_s2]: `/usr/local/sbin/nwstat.pl 192.168.2.4 s2 s8`
MaxBytes[nw6_s2]: 2000000000
ShortLegend[nw6_s2]:  
YLegend[nw6_s2]: Cache Buffers
LegendI[nw6_s2]: Number of Free Cache Buffers
LegendO[nw6_s2]: Number of Total Cache Buffers
Title[nw6_s2]: Number of Cache Buffers for Server NW6
PageTop[nw6_s2]: Number of Cache Buffers for Server NW6 </H4></center>

# s20 = Max service processes
# s21 = Current service processes
Target[nw6_proc]: `/usr/local/sbin/nwstat.pl 192.168.2.4 s21 s20`
MaxBytes[nw6_proc]: 2000000
ShortLegend[nw6_proc]:  
YLegend[nw6_proc]: Processes
LegendI[nw6_proc]: Current service processes
LegendO[nw6_proc]: Max service processes
Title[nw6_proc]: Service processes analysis for Server NW6
PageTop[nw6_proc]: Service processes analysis for Server NW6 </H4></center>

# s15 = Packet receive buffer count
# s16 = Maximum packet receive buffer count
Target[nw6_packet]: `/usr/local/sbin/nwstat.pl 192.168.2.4 s15 s16`
MaxBytes[nw6_packet]: 2000000
ShortLegend[nw6_packet]:  
YLegend[nw6_packet]: Buffers
LegendI[nw6_packet]: Current packet buffers
LegendO[nw6_packet]: Max packet buffers
Title[nw6_packet]: Packet receive buffers count for Server NW6
PageTop[nw6_packet]: Packet receive buffers count for Server NW6 </H4></center>


#---------------------------------------------------------------------------------
# 				DISKS
#---------------------------------------------------------------------------------
Target[nw6_vol-data]: `/usr/local/sbin/nwstat.pl 192.168.2.4 VKUDATA VKSDATA`
MaxBytes[nw6_vol-data]: 500000000000
kilo[nw6_vol-data]: 1024
kMG[nw6_vol-data]: k,M,G,T,P
ShortLegend[nw6_vol-data]: b
YLegend[nw6_vol-data]: Disk space
LegendI[nw6_vol-data]: Kbytes used on volume DATA
LegendO[nw6_vol-data]: Size of volume DATA in Kbytes
Title[nw6_vol-data]: Volume DATA statistics for server  NW6
PageTop[nw6_vol-data]: Volume DATA statistics for Server NW6 </H4></center>

Target[nw6_pur_vol-data]: `/usr/local/sbin/nwstat.pl 192.168.2.4 VKPDATA VKNPDATA`
MaxBytes[nw6_pur_vol-data]: 1000000000000
kilo[nw6_pur_vol-data]: 1024
kMG[nw6_pur_vol-data]: k,M,G,T,P
ShortLegend[nw6_pur_vol-data]: b
YLegend[nw6_pur_vol-data]: Purgable files
LegendI[nw6_pur_vol-data]: Kbytes of purgable files on volume DATA
LegendO[nw6_pur_vol-data]: Kbytes of not-yet-purgable files on volume DATA
Title[nw6_pur_vol-data]: Volume DATA purgable files statistics for server  NW6
PageTop[nw6_pur_vol-data]: Volume DATA purgable files statistics for Server NW6 </H4></center>

Target[nw6_vol-sys]: `/usr/local/sbin/nwstat.pl 192.168.2.4 VKUSYS VKSSYS`
MaxBytes[nw6_vol-sys]: 500000000000
kilo[nw6_vol-sys]: 1024
kMG[nw6_vol-sys]: k,M,G,T,P
ShortLegend[nw6_vol-sys]: b
YLegend[nw6_vol-sys]: Disk space
LegendI[nw6_vol-sys]: Kbytes used on volume SYS
LegendO[nw6_vol-sys]: Size of volume SYS in Kbytes
Title[nw6_vol-sys]: Volume SYS statistics for server NW6
PageTop[nw6_vol-sys]: Volume SYS statistics for Server NW6 </H4></center>


Из него понятно, как можно мониторить процессор, ОЗУ, буфера на Netware-сервере через mrtgext, если это кому-нибудь еще надо.

Мониторинг Novell (уже не, но не суть) OES

.
OES — это Open Enterprise Server. В основе OES лежит SUSE Linux, поэтому базовый мониторинг тут те же, что и для linux, а вот как мониторить дополнительные сервисы сейчас опишу.

Опять же на первом месте будет мониторинг томов:

get-voldata.pl
#!/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,
         "v=s" => \$volname, "volname"  => \$volname,
         "w=s" => \$opt_w, "warning=s"  => \$opt_w,
         "c=s" => \$opt_c, "critical=s" => \$opt_c,
         "m=s" => \$opt_mode, "mode=s" => \$opt_mode,
         "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_usage();  exit $ERRORS{'ERROR'}; };

($opt_c) || ($opt_c = shift) || ($opt_c = 95);
my $critical = $1 if ($opt_c =~ /([0-9]+)/);

($opt_w) || ($opt_w = shift) || ($opt_w = 90);
my $warning = $1 if ($opt_w =~ /([0-9]+)/);

($volname) || ($volname = shift) || ($volname = 'SYS');
my $vname = $volname;

my $mode = 0;

($opt_mode) || ($opt_mode = shift) || ($opt_mode = 'nagios');
if ($opt_mode =~ /mrtg/i) { $mode = 1; }

my $servname = $host;
my $uptime   = '';

my $community = 'ISOlanf';
my $SNMP 	  = "/usr/local/bin/snmpget  -v 2c -c $community";
my $snmpwalk 	  = "/usr/local/bin/snmpwalk -v 2c -c $community";

my $OID_NAME   = 'SNMPv2-MIB::sysName.0';
my $OID_UPTIME = '.1.3.6.1.2.1.1.3.0';
my $OID_SIZE = 'HOST-RESOURCES-MIB::hrStorageSize';
my $OID_USED = 'HOST-RESOURCES-MIB::hrStorageUsed';
my $OID_DESC = 'HOST-RESOURCES-MIB::hrStorageDescr';

my $pool = '.pools/';

my $n=getvol($host, $vname);
if ($n == 0) { exit $ERRORS{'UNKNOWN'}; }
my $percent = getInfo($host, $n);

if ($mode == 0) { #nagios
  if ($percent > 100) { exit $ERRORS{'ERROR'}; }
  printf "Used: %.2f\%\n", $percent;

  if (($percent > $critical) || ($percent == 0)) { exit $ERRORS{'CRITICAL'}; } # Critical!
  if ($percent >  $warning) { exit $ERRORS{'WARNING'};  } #  Warning!
  exit $ERRORS{'OK'};   #  Okay
};
if ($mode == 1) { # mrtg   
  print $percent;
  exit (0);
}
#  ================================================================
sub getvol {
  my $ip = shift;
  my $v  = shift;

  my $ret = 0; 

  my $seek = $pool.$v;
  my @n = getDesc($ip);

  foreach $l (@n) {
    if 	($l =~ /$OID_DESC\.(\d+).+$seek/i) {
       return $1;
    }
  };
  
  return $ret;
}
#  ================================================================
sub getInfo {
  my $ip  = shift;
  my $n   = shift;
  my $ret = '';

  $servname = getSNMPdata($ip, $OID_NAME);
  my $size  = getSNMPdata($ip, $OID_SIZE.'.'.$n);  
  my $used  = getSNMPdata($ip, $OID_USED.'.'.$n);  
  if ($size < 1) { return ''; }
  $ret = ($used/$size)*100;
  if ($mode == 1) { 
    $used = $used*4;
    $size = $size*4;    
    $ret = "$used\n$size\n$uptime\n$servname\n";
  }
  return $ret;
}

#  ================================================================
sub getSNMPdata {
  my $ip  = shift;
  my $snmpquery = shift;
  my $q, $dat;

  $q = "$SNMP $ip $snmpquery | awk '{print \$4}'";
  $dat = `$q`;
  chomp $dat;
  if (length($dat) < 1) {
    return 'U';
  }
  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 getDesc {
  my $ip = shift;
  my @ret = '';    
  my $q = "$snmpwalk $ip $OID_DESC";

  @ret = `$q`;
  return @ret;
};
#  ================================================================
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, 2013 \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>   = Used mode - nagios -> return percents of volume used, mrtg -> return used and max size of volume for mrtg\nBy default use 'nagios'\n\n";
        support();
};
#  ================================================================


Скрипт этот у меня универсальный для nagios и для mrtg, поэтому с параметром -m mrtg он выдает циферки размера (занято и всего) тома, как их ждет mrtg, а без оного или с параметром -m nagios выдает ответ, типичный для плагина nagios.

В качестве параметров он принимает имя или ip сервера и имя тома. Тома ищутся в списке смонтированных, точки монтирования .pools/ИМЯТОМА (технические для OES) игнорируются. Если том не найден, возвращается размер 0 и ситуация CRITICAL (у нас много «внешних» — fc или iscsi — томов и надо ловить случаи их отпадения).

По-умолчанию анализируется том SYS (тяжкое наследие Netware, есть у каждого OES-сервера, но совершенно не нужен и не интересен).

Используется в nagios обычным образом

define command{
        command_name    check_oesvolsize
        command_line       $USER1$/get-voldata.pl -H $HOSTADDRESS$ -v $ARG1$ -w $ARG2$ -c $ARG3$
        }

в mrtg так:

 `/data/rrdtool/oes/get-voldata.pl -H ip-сервера -v ИМЯТОМА -m mrtg`


Теперь о сервисе печати. Принтеры можно мониторить самостоятельно, но если у вас iPrint, то интересно мониторить принтеры «с точки зрения сервера». И для этого есть скриптик:

check_iprinters.pl
#!/usr/bin/perl -w
#
# @File check_iprinters.pl
# @Author dbenjamin
# @Created Aug 4, 2015 2:59:02 PM
# Licence : GPL - http://www.gnu.org/licenses/lgpl-3.0.html
#

use strict;
use LWP::Simple;
use HTML::TreeBuilder;
use Getopt::Long;
my $nagios_plugins_utils =
#  "/usr/lib/nagios/plugins/utils.pm";    #used to test for the library
  "/usr/local/libexec/nagios/utils.pm";    #used to test for the library
die "\nLooking for nagios utils.pm at $nagios_plugins_utils"
  unless ( -e $nagios_plugins_utils );
use lib "/usr/local/libexec/nagios";       #use just the path here
use utils qw(%ERRORS $TIMEOUT);

my ( $opt_h, $opt_I, $opt_Q, $opt_v, $opt_P, $opt_V );
my ( $printer_state, $accepting_jobs, $jobs_scheduled );
my $i = 0;                               #iteration holder
$opt_P = '631';

alarm($TIMEOUT);

sub print_version {
    print "File:     check_iprinters.pl\n";
    print "Author:   dbenjamin\n";
    print "Created:  Aug 4, 2015\n";
    print "Release:  0.0.1\n";
    print "Tested against Novell iPrint Server 6.7.0.20150629-0.6.6, ";
    print "running on SLES 11, SP3 with OES 11, SP2.\n";
    exit $ERRORS{'UNKNOWN'};
}

sub print_exit {
    print
"Usage: $0 -I <host address> -Q <queue name> [-P <port> default=631] [-v enable verbose] [--version]\n\n";
    exit $ERRORS{'UNKNOWN'};
}

sub print_verbose {
    print "Printer State:  $printer_state\n";
    print "Printer is Accepting Jobs:  $accepting_jobs\n";
    print "Jobs Scheduled:  $jobs_scheduled\n";
}

GetOptions(
    "version" => \$opt_V,
    "h"       => \$opt_h,
    "help"    => \$opt_h,
    "I=s"     => \$opt_I,
    "Q=s"     => \$opt_Q,
    "P:s"     => \$opt_P,
    "v"       => \$opt_v,
) or print_exit;

if ($opt_V) {
    print_version;
}

if ($opt_h) {
    print_exit;
}

if ( !$opt_I ) {
    print "No Host address specified\n";
    print_exit;
}
if ( !$opt_Q ) {
    print "No Queue name specified\n";
    print_exit;
}

if ( ( $opt_I eq '' ) or ( $opt_Q eq '' ) ) { print_exit; }
my $tree   = new HTML::TreeBuilder->new;
my $url    = "http://$opt_I:$opt_P/ipp/$opt_Q";
my $result = get($url);
die "\nCouldn't get $url" unless defined $result;
$tree->parse($result);
my @tbrows = $tree->look_down( '_tag', 'TR' );
die "No response, check the URL for errors:  $url\n\n" unless @tbrows;
foreach $i ( 2 .. 4 ) {
    my @td = $tbrows[$i]->look_down( '_tag', 'TD' );
    if ( $i == 2 ) { $printer_state  = $td[1]->as_text; }
    if ( $i == 3 ) { $accepting_jobs = $td[1]->as_text; }
    if ( $i == 4 ) { $jobs_scheduled = $td[1]->as_text; }
}
if ( ( $printer_state =~ /error/i ) & ( $printer_state =~ /empty/i ) ) {
    if   ($opt_v) { print_verbose }
    else          { print "$printer_state\n\n"; }
    exit $ERRORS{'WARNING'};
}
else {
    if ( $printer_state =~ /error/i ) {
        if   ($opt_v) { print_verbose }
        else          { print "$printer_state\n\n"; }
        exit $ERRORS{'CRITICAL'};
    }
}
if ( $opt_v ) { print "jobs=$jobs_scheduled\n"; }
exit $ERRORS{'OK'};

Скрипт не мой, но чертовски полезный. В нагиос его можно использовать так:

define command{
        command_name    check_iprinter_q
        command_line    $USER1$/check_iprinters.pl -I $HOSTADDRESS$ -Q $ARG1$ $ARG2$
        }

define service{
        use                             generic-service
        name                            iprint_q
        host_name                       iprint
        contacts                        printer-admins
        check_period                    24x7                    
        max_check_attempts              3
        normal_check_interval           10                     
        retry_check_interval            5  
        notifications_enabled           0
        notification_options            c,r
        notification_interval           60                 
        notification_period             workhours
        icon_image                      printer.gif
        flap_detection_enabled          0
        register                        0
        }

define service{
        use                             iprint_q
        service_description             AHO-2430
        check_command                   check_iprinter_q!AHO-2430!-v
        }

Когда принтеров у вас несколько десятков (у меня их ближе к сотне), вести такой список непросто, но зато вы сразу видите, где кончилась бумага или картридж, какой принтер уже 40+ дней отключен (у нас 40 дней отключения — порог на снятие принтера) или стоит много дней без бумаги или картриджа. Польза есть.

Фух, много получилось. Пока остановлюсь. Если статья понравится, будет вторая часть — про мониторинг ИБП, Synology, vmware и принтеров, но уже как принтеров, а не как объектов iPrint.
Tags:
Hubs:
+8
Comments 19
Comments Comments 19

Articles