Съём статистики по SNMP с устройств
Автор: lissyara.
Оригинал: http://www.lissyara.su/articles/freebsd/traffic_count/get_switch_statistic_trough_snmp/
Немного предистории, с чего началось всё это. Провайдер мой, начал выкатывать счета за траффик, которые не лезли ни в одни ворота - по моим данным за день полторы-две сотни мегабайт траффика, по его - больше гигабайта. Особенно порадовали выходные - у меня мег с копейками, у него - полторы сотни. Звонил, пытался что-то выяснить, разобраться. В итоге, полуобкуренный товарищ из суппорта заявил мне, - "мы не считаем ваш траффик, мы построили график по предыдущим месяцам, и по нему выставляем счета...". Хорошо я сидел в этот момент.... В итоге написали письмо с требованием разобраться, а я озадачился темой - проверить - то ли я считаю. Может моя считалка где врёт... Раз, так в десять... Вначале накрутил правил в IPFW. C trafd, которым я считал, всё сошлось. Но на этом я не успокоился, вспомнил, что можно считать по SNMP, собственно, так все и делают. Тем более что у меня стоял свич AT-8326GB, шикарная машинка, с WEB-интерфейсом, телнетом, SNMP и поддержкой виртуальных свичей. Оставалось только включить в нём SNMP и найти чем считать. Конечно, для такого есть куча программ, и мильён скриптов. Но - коммунисты не ищут лёгких путей! Поэтому, как обычно, я пошёл своим.
SNMP - Simple Network Management Protocol, он работает над протоколом UDP и позволяет управляющим станциям собирать информацию о положении в сети. Для работы с SNMP устанавливаем NET-SNMP. Почему он? Просто он у меня уже стоял, поставился, когда я устанавливал cacti, когда впервые заинтересовался возможностями протокола SNMP и устройств, его поддерживающих. Если захотите что-то другое, то в портах немало программ способных работать с этипм протоколом, есть модули для PERL и PHP, их можно разыскать там же - в портах.
Ставим NET-SNMP./usr/ports/>cd /usr/ports/net-mgmt/net-snmp
/usr/ports/net-mgmt/net-snmp/>make && make install && make clean
Если Вы хотите и с этой машины снимать инфу - то надо кой-чё поднастраивать, чтоб запускался агент SNMP, в моём случае - ничё не надо делать. Я буду собирать на эту машину инфу, а не с неё.
Ставим перл и модуль ..... /usr/ports/net-mgmt/net-snmp/>cd ../../lang/perl5.8/
/usr/ports/lang/perl5.8/>make && make install && make clean
после чего даём команду rehash и use.perl port. Затем ставим модуль для работы с SNMP/usr/ports/>cd /usr/ports/net-mgmt/p5-Net-SNMP
/usr/ports/net-mgmt/p5-Net-SNMP/>make && make install && make clean
и модуль для работы с MySQL (ставить надо либо универсальный, либо той версии, которой MySQL - я пробовал ставить универсальный, но он за собой потащил MySQL4.1-server, а я пользуюсь 4.0) /usr/ports/databases/>cd p5-DBD-mysql40
/usr/ports/databases/p5-DBD-mysql40/>make && make install && make clean
Теперь, когда всё нужное ПО установлено, можно поковыряться в самом свиче по SNMP. Основные сведения по SNMP достаточно легко найти в рунете, многое интуитивно понятно само. В частности можно расширить сферу применения - мониторить не только траффик но и многое другое - размер дисков, занятое и свободное место, загрузку процессоров.....
Итак, смотрим, что мы можем поиметь, с этого свича (инфы с него лезет реально немеряно - так что пишите всё в текстовый файл, чтоб на досуге разобраться, или если знаете что ищете - разбирайтесь сами). //>snmpwalk -c my_community -v 1 192.168.20.253 .
SNMPv2-MIB::sysDescr.0 = STRING: AT-8326GB
SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.207.1.4.72
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (138565036) 16 days, 0:54:10.36
SNMPv2-MIB::sysContact.0 = STRING: телефон 219
SNMPv2-MIB::sysName.0 = STRING: СВИЧ
SNMPv2-MIB::sysLocation.0 = STRING: Серверная, второй этаж
SNMPv2-MIB::sysServices.0 = INTEGER: 2
IF-MIB::ifNumber.0 = INTEGER: 53
IF-MIB::ifIndex.1 = INTEGER: 1
IF-MIB::ifIndex.2 = INTEGER: 2
IF-MIB::ifIndex.3 = INTEGER: 3
IF-MIB::ifIndex.4 = INTEGER: 4
IF-MIB::ifIndex.5 = INTEGER: 5
IF-MIB::ifIndex.6 = INTEGER: 6
^C
Собственно дальше смотреть нечего, у меня оно лезло минут 40... Объём всей инфы, в виде текста, составил 4 мб... Правда полезного там - доли процента (с другой стороны - там всё нужное и полезное, но не мне и не сейчас). Дальше, вытаскиваем интерфейсы, имеющиеся на свиче//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.22
IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.2 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.3 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.4 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.5 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.6 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.7 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.8 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.9 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.10 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.11 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.12 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.13 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.14 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.15 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.16 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.17 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.18 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.19 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.20 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.21 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.22 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.23 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.24 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.25 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.26 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.27 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.28 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.29 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.30 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.31 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.32 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.33 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.34 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.35 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.36 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.37 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.38 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.39 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.40 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.41 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.42 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.43 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.44 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.45 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.46 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.47 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.48 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.49 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.50 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.51 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.52 = OID: SNMPv2-SMI::zeroDotZero
IF-MIB::ifSpecific.53 = OID: SNMPv2-SMI::zeroDotZero
Оказалось 53 интерфейса... Любопытно, портов-то всего 52 (48 на 100 мегабит и 4 по 1000). По здравому размышлению, решил, что один из интерфейсов - это собственно проц приборины. Но вот какой? Логичней всего было б первый, последний, или ровно посредине диапазона. Попал с первого раза - он был последним :) //>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.2.53
IF-MIB::ifDescr.53 = STRING: AT-8326GB CPU Ethernet Network Interface
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.2.1
IF-MIB::ifDescr.1 = STRING: AT-8326GB 10/100 Mbps Ethernet Network Interface 1
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.2.25
IF-MIB::ifDescr.25 = STRING: AT-8326GB 1000 Mbps Ethernet Network Interface 25
И это действительно оказался CPU свича, а не секретный порт, нераспаянный на передней панели :) Смотрим, какие данные можно снять по интерфейсам: //>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.1.1
IF-MIB::ifIndex.1 = INTEGER: 1
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.2.1
IF-MIB::ifDescr.1 = STRING: AT-8326GB 10/100 Mbps Ethernet Network Interface 1
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.3.1
IF-MIB::ifType.1 = INTEGER: ethernetCsmacd(6)
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.4.1
IF-MIB::ifMtu.1 = INTEGER: 1514
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.5.1
IF-MIB::ifSpeed.1 = Gauge32: 100000000
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.6.1
IF-MIB::ifPhysAddress.1 = STRING: 0:c:46:9f:a9:59
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.7.1
IF-MIB::ifAdminStatus.1 = INTEGER: up(1)
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.8.1
IF-MIB::ifOperStatus.1 = INTEGER: down(2)
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.9.1
IF-MIB::ifLastChange.1 = Timeticks: (111215050) 12 days, 20:55:50.50
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.10.1
IF-MIB::ifInOctets.1 = Counter32: 1185896152
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.11.1
IF-MIB::ifInUcastPkts.1 = Counter32: 7447462
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.12.1
IF-MIB::ifInNUcastPkts.1 = Counter32: 1178
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.13.1
IF-MIB::ifInDiscards.1 = Counter32: 0
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.14.1
IF-MIB::ifInErrors.1 = Counter32: 0
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.15.1
IF-MIB::ifInUnknownProtos.1 = Counter32: 0
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.16.1
IF-MIB::ifOutOctets.1 = Counter32: 3490598015
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.17.1
IF-MIB::ifOutUcastPkts.1 = Counter32: 7962148
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.18.1
IF-MIB::ifOutNUcastPkts.1 = Counter32: 82020
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.19.1
IF-MIB::ifOutDiscards.1 = Counter32: 0
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.20.1
IF-MIB::ifOutErrors.1 = Counter32: 0
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.21.1
IF-MIB::ifOutQLen.1 = Gauge32: 192
//>snmpwalk -c my_community -v 1 192.168.20.253 .1.3.6.1.2.1.2.2.1.22.1
IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero
Инфы немало. Есть и самое главное, чего ради всё это и затевалось - входящий и исходящий траффик по интерфейсам. Ну, и всякая дополнительная мишура, типа, состояние интерфейса - up или down, размер MTU, скорость, MAC-адрес, сколько было входящих-исходящих пакетов, ошибок, и много ещё чего. Для грамотного изъёма всего этого хозяйства и хранения его в БД был написан следующий скриптик на perl: #!/usr/bin/perl -w
# вводим переменные
# хост, который будем опрашивать
$snmp_host = '192.168.8.253';
# сообщество
$snmp_community = 'derzhava';
# начало МИБ`а
$snmp_part_MIB = '.1.3.6.1.2.1.2.2.1';
# максимальный номер интерфейса
$IF_max = 53;
# MySQL хост
$host = 'localhost';
# MySQL юзер
$user = 'SNMP';
# MySQL пароль
$password = 'SNMP';
# MySQL база данных
$database = 'SNMP';
# подрубаем модуль, отвечающий за SNMP
use Net::SNMP;
# подрубаем модуль для работы с MySQL
use Mysql;
# достаём время
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
$year = $year + 1900;
$mon = $mon + 1;
$unix_time = time;
# Коннектимся к MySQL
$dbh = Mysql->Connect($host,$database,$password,$user);
# Создаём в БД таблицу для хранения итогов
$MySQL_query = "CREATE TABLE IF NOT EXISTS `" . $year . "_SNMP`
(`unic_id` INT(12) NOT NULL AUTO_INCREMENT ,
`SNMP_date` VARCHAR(10) NOT NULL ,
`SNMP_time` VARCHAR(8) NOT NULL ,
`SNMP_unix_time` INT(32) NOT NULL ,
`SNMP_IF` INT(2) NOT NULL ,
`SNMP_IF_speed` int(9) NOT NULL ,
`SNMP_IF_status` ENUM('1','2','3') NOT NULL ,
`SNMP_IF_traff_in` INT(255) NOT NULL ,
`SNMP_IF_traff_out` INT(255) NOT NULL ,
PRIMARY KEY (`unic_id`),
KEY `SNMP_unix_time` (`SNMP_unix_time`),
KEY `SNMP_date` (`SNMP_date`)
)TYPE = MYISAM
COMMENT='Траффик по SNMP за " . $year . " год'";
$dbh->Query("$MySQL_query") or die $Mysql::db_errstr;
# Создаём в БД таблицу для хранения временных данных
$MySQL_query = "CREATE TABLE IF NOT EXISTS `SNMP_tmp_table`
(`SNMP_unix_time` INT(255) NOT NULL,
`SNMP_IF` INT(255) NOT NULL,
`SNMP_IF_traff_in_absolute` INT(255) NOT NULL,
`SNMP_IF_traff_out_absolute` INT(255) NOT NULL
) TYPE = MYISAM
COMMENT = 'Временная таблица для данных SNMP'";
$dbh->Query("$MySQL_query") or die $Mysql::db_errstr;
# Очищаем временную таблицу, после использованием
#$MySQL_query = "TRUNCATE TABLE `SNMP_tmp_table`";
#$dbh->Query("$MySQL_query") or die $Mysql::db_errstr;
# открываем сессию SNMP
($session,$error)=Net::SNMP->session(Hostname => $snmp_host,
Community => $snmp_community);
die "session error: $error" unless($session);
# запускаем цикл по перебору всех доступных сетевых интерфейсов устройства
for($i = 1; $i < $IF_max + 1; $i++){
# строим MIB для трафика
$snmp_MIB_traff_in = $snmp_part_MIB . ".10." . $i;
$snmp_MIB_traff_out = $snmp_part_MIB . ".16." . $i;
$traff_in = $session->get_request("$snmp_MIB_traff_in");
$traff_out = $session->get_request("$snmp_MIB_traff_out");
die "request error: " . $session->error unless(defined $traff_in);
# результаты по траффику
$traff_in = $traff_in->{"$snmp_MIB_traff_in"};
$traff_out = $traff_out->{"$snmp_MIB_traff_out"};
# строим MIB для примечания к интерфейсу
#$snmp_MIB_descr = $snmp_part_MIB . ".2." . $i;
#$IF_descr = $session->get_request("$snmp_MIB_descr");
#die "request error: " . $session->error unless(defined $IF_descr);
# результаты - примечание для интерфейса
#$IF_descr = $IF_descr->{"$snmp_MIB_descr"};
# строим MIB для MTU интерфейса
#$snmp_MIB_MTU = $snmp_part_MIB . ".4." . $i;
#$IF_MTU = $session->get_request("$snmp_MIB_MTU");
#die "request error: " . $session->error unless(defined $IF_MTU);
# результаты - MTU интерфейса
#$IF_MTU = $IF_MTU->{"$snmp_MIB_MTU"};
# строим MIB для скорости интерфейса
$snmp_MIB_speed = $snmp_part_MIB . ".5." . $i;
$IF_speed = $session->get_request("$snmp_MIB_speed");
die "request error: " . $session->error unless(defined $IF_speed);
# результаты - скорость интерфейса
$IF_speed = $IF_speed->{"$snmp_MIB_speed"};
# строим MIB для MAC-адреса интерфейса
#$snmp_MIB_MAC = $snmp_part_MIB . ".6." . $i;
#$IF_MAC = $session->get_request("$snmp_MIB_MAC");
#die "request error: " . $session->error unless(defined $IF_MAC);
# результаты - MAC-адрес интерфейса
#$IF_MAC = $IF_MAC->{"$snmp_MIB_MAC"};
# строим MIB для состояния интерфейса
$snmp_MIB_status = $snmp_part_MIB . ".8." . $i;
$IF_status = $session->get_request("$snmp_MIB_status");
die "request error: " . $session->error unless(defined $IF_status);
# результаты - состояние интерфейса
$IF_status = $IF_status->{"$snmp_MIB_status"};
# лезем в БД с целью достать предыдущие значения счётчиков
# для начала достаём максимальный UNIX TIME для этого интерфейса
$MySQL_query = "SELECT MAX(`SNMP_unix_time`)
FROM `SNMP_tmp_table` WHERE `SNMP_IF`='" . $i . "'";
$sth = $dbh->query("$MySQL_query") or die $Mysql::db_errstr;
@row = $sth->fetchrow;
# достаём данные от предыдущего съёма статистики
$MySQL_query = "SELECT `SNMP_IF_traff_in_absolute`,`SNMP_IF_traff_out_absolute`
FROM `SNMP_tmp_table` WHERE `SNMP_IF`='" . $i . "'
AND `SNMP_unix_time`='" . $row[0] . "'";
$sth = $dbh->query("$MySQL_query") or die $Mysql::db_errstr;
@row = $sth->fetchrow;
# Проверяем что в $row[1] и $row[0]- число или нет
# у меня из-за этого ошибки лезли частенько - два-три
# раза за сутки
if($row[1] =~ /[0-9]/){
# число. Ничего не делаем
}else{
# Там не число. Приравниваем к 0 - будет число
$row[1] = 0;
}
if($row[0] =~ /[0-9]/){
# число. Ничего не делаем
}else{
# Там не число. Приравниваем к 0 - будет число
$row[0] = 0;
}
# проверяем, больше, или меньше текущее значение, чем предыдущее,
# на случай, что счётчики сбрасывались, между измерениями
if($traff_in >= $row[0]){
# всё пучком, счётчик не сбрасывался, считаем разницу
$delta_traff_in = $traff_in - $row[0];
}else{
# счётчик сбросился - данные по этому интерфейсу
# за последнюю минуту считаем пропавшими
#$delta_traff_in = $traff_in;
}
if($traff_out >= $row[1]){
# всё пучком, счётчик не сбрасывался, считаем разницу
$delta_traff_out = $traff_out - $row[1];
}else{
# счётчик сбросился - данные по этому интерфейсу
# за последнюю минуту считаем пропавшими
#$delta_traff_out = $traff_out;
}
# Засовываем данные во временную таблицу
$MySQL_query = "INSERT INTO `SNMP_tmp_table`
(`SNMP_unix_time`,`SNMP_IF`,`SNMP_IF_traff_in_absolute`,
`SNMP_IF_traff_out_absolute`) VALUES ('" . $unix_time . "',
'" . $i . "','" . $traff_in . "','" . $traff_out ."')";
$dbh->query("$MySQL_query") or die $Mysql::db_errstr;
# засовываем даные в постоянную таблицу
$MySQL_query = "INSERT INTO `" . $year . "_SNMP`
(`SNMP_date`,`SNMP_time`,`SNMP_unix_time`,`SNMP_IF`,
`SNMP_IF_speed`,`SNMP_IF_status`,`SNMP_IF_traff_in`,
`SNMP_IF_traff_out`) VALUES
('" . $year . "-" . $mon . "-" . $mday . "',
'" . $hour . ":" . $min . ":00',
'" . $unix_time . "','" . $i ."',
'" . $IF_speed . "','" . $IF_status . "',
'" . $delta_traff_in . "','" . $delta_traff_out . "')";
$dbh->query("$MySQL_query") or die $Mysql::db_errstr;
}
# Отваливаемся от SNMP
$session->close;
# Удаляем из временной таблицы все записи,
# кроме тех, что добавили только что
$MySQL_query = "DELETE FROM `SNMP_tmp_table`
WHERE `SNMP_unix_time`!='" . $unix_time . "'";
$dbh->query("$MySQL_query") or die $Mysql::db_errstr;
1;
Ну, вот и всё. При первом запуске будет ругаться жутким образом - временная таблица пуста, но на первом же запуске он её заполнит, и при следующих должен отрабатывать без ошибок и сообщений. Также после первого запуска нужно очистить, или уничтожить таблицу с именем `ТЕКУЩИЙ_ГОД_SNMP` - он её заполняет содержимым счётчиков, а не разницей между запусками. При следующем запуске он её сделает заново и будет заполнять правильно. В crontab его, и пусть пашет с периодичностью, кому как нравится (мне, вот, раз в минуту нравится.). В вышеприведённом скрипте есть закомментированные места - я их оставил так как есть - может кому понадобиться, да и сам подумываю насчёт более подробной статистики.... Несмотря на всю специфичность скриптика, если у кого-то возникнет необходимость, он достаточно легко рихтанёт его под свои нужды.
P.S. Ненавижу перл.