Эта статья описывает процесс настройки мониторинга источников бесперебойного питания (ИБП) с использованием системы мониторинга Zabbix. Основные моменты:
Автор решал задачу мониторинга разных моделей ИБП (Ippon, Powercom, Krauler) недорогим способом.
Вместо SNMP-модулей было решено использовать подключение по последовательным портам (RS-232).
Описывается процесс подбора и изготовления кабелей для подключения ИБП к серверам.
Автор разработал программу на C++ для опроса ИБП через COM-порт и получения данных о состоянии.
Рассказывается о настройке виртуальной машины на VMware ESXi для размещения программы опроса и скриптов.
Описывается процесс отправки полученных данных в Zabbix с использованием утилиты zabbixsender и самописного скрипта на Perl.
Приводится bash-скрипт для автоматизации процесса опроса ИБП и отправки данных в Zabbix.
Обсуждаются некоторые проблемы и их решения, например, коррекция данных о напряжении батарей для разных моделей ИБП.
Статья представляет собой подробное техническое руководство по реализации бюджетного решения для мониторинга ИБП в среде Zabbix. CopyRetry
#include <stdio.h> /* ���������������������� �������������������� ����������/������������ */
#include <string>
#include <iostream>
#include <cstring>
using namespace std;
#include <unistd.h> /* �������������������� ���������������������� �������������� UNIX */
#include <fcntl.h> /* �������������������� �������������������� �������������� */
#include <errno.h> /* �������������������� ���������� ������������ */
#include <termios.h> /* �������������������� �������������������� POSIX-�������������������� */
#include <sys/types.h>
#include <sys/stat.h>
int fd; /* ���������������� �������������������� ������ ���������� */
char buf[512];/*������������ �������������� ���� �������������� ������������ ���������������������� ������������*/
int main (int argc, char* argv[])
{
int iIn,iOut;
string UPSAnswer;
if (argc>=2)
{
fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY); /*'open_port()' - ������������������ �������������������������������� �������� */
if (fd == -1) {
printf("error port\n");
perror("open_port: Unable to open port - "); }
else
{
struct termios options; /*������������������ ������ ������������������ ����������*/
tcgetattr(fd, &options); /*������������ ������������������ ����������*/
cfsetispeed(&options, B2400); /*������������������ ���������������� ����������*/
cfsetospeed(&options, B2400); /*������������������ ���������������� ����������*/
options.c_cflag &= ~PARENB; /*�������� ���������������� ����������������*/
options.c_cflag &= ~CSTOPB; /*�������� 2-�� ������������, ������ 1 ��������������*/
options.c_cflag &= ~CSIZE; /*�������� �������������� ����������*/
options.c_cflag |= CS8; /*������ 8������*/
tcsetattr(fd, TCSANOW, &options); /*�������������������� �������������������� ����������*/
}
char Params[64];
if(argc>=3)
{
if (!strcmp(argv[2],"status"))
{
iOut = write(fd, "Q1\r", 3);
usleep(800000);
if (iOut < 0) fputs("write() of 4 bytes failed!\n", stderr);
iIn=read(fd,buf,250); /*������������ �������������������� ������������ ���� ����������*/
strncpy(Params,&buf[38],11);
Params[46]=0;
}
else if (!strcmp(argv[2],"help"))
{
printf ("Денег нет. Но вы держитесь!\r\n");
}
else if (!strcmp(argv[2],"name"))
{
iOut = write(fd, "I\r", 3);
usleep(800000);
if (iOut < 0) fputs("write() of 4 bytes failed!\n", stderr);
iIn=read(fd,buf,250); /*������������ �������������������� ������������ ���� ����������*/
strncpy(Params,&buf[0],60);
}
else if (!strcmp(argv[2],"stat2"))
{
iOut = write(fd, "F\r", 3);
usleep(800000);
if (iOut < 0) fputs("write() of 4 bytes failed!\n", stderr);
iIn=read(fd,buf,250); /*������������ �������������������� ������������ ���� ����������*/
strncpy(Params,&buf[1],60);
Params[20]=0;
}
else
{
printf ("Щта? \r\n");
}
}
else
{
/* ������������ ������������ ���� Q1 */
usleep(1800);
iIn=read(fd,buf,250);
usleep(1800);
iOut = write(fd, "Q1\r", 3);
usleep(800000);
if (iOut < 0) fputs("write() of 4 bytes failed!\n", stderr);
iIn=read(fd,buf,250); /*������������ �������������������� ������������ ���� ����������*/
strncpy(Params,&buf[1],36);
Params[36]=' ';
}
close(fd);
printf("%s\r\n",Params);
}
else
printf("Usage %s /dev/ttySx", argv[0]);
}
root@zabbix:~# ./uniups /dev/ttyS0
204.4 204.4 204.4 035 49.9 54.8 54.5
root@zabbix:~# ./uniups /dev/ttyS0 name
root@zabbix:~# ./uniups /dev/ttyS1 name
#POWERCOM SXL-2000A LCD V4.3
root@zabbix:~# ./uniups /dev/ttyS1
216.3 216.3 216.3 000 50.0 54.2 30.0
root@zabbix:~# ./uniups /dev/ttyS1 stat2
220.0 009 048.0 50.0
root@zabbix:~#
zabbixsender -z адрессервера -p 10051 -s ИмяИБПвЗаббикс -k Ключ -o Значение
С отправкой данный получился зоопарк на все случаи жизни. На сервере Заббикс воспользовался утилитой zabbixsender для ускорения процесса.
zabbixsender -z адрессервера -p 10051 -s ИмяИБПвЗаббикс -k Ключ -o Значение
Вся соль заключается в кодировании данных в Base64. Для проверки я использовал команду
echo "<req>\n<host>S3JhdWxlck1lbW9SVDIwMDA=</host>\n<key>RnJlcQ==</key>\n<data>NDkuNA==</data>\n</req>\n" | nc -q 0 192.168.53.23 10051
�������������������������� ������������ �� ������������ zabbix_sender.pl �� ���������������� ���� ��������������, ������������ �� �������� ���������������� ������:
#!/usr/bin/perl
use IO::Socket;
use IO::Select;
use MIME::Base64;
my ($zabbixserver,$hostname,$item,$data) = @_;
$zabbixserver= @ARGV[0];
$hostname= @ARGV[1];
$item= @ARGV[2];
$data= @ARGV[3];
my $timeout=10;
my $request=sprintf("<req>\n<host>%s</host>\n<key>%s</key>\n<data>%s</data>\n</req>\n",
encode_base64($hostname),encode_base64($item),encode_base64($data));
my $sock = new IO::Socket::INET ( PeerAddr => $zabbixserver, PeerPort => '10051', Proto => 'tcp', Timeout => $timeout);
die "Could not create socket: $!\n" unless $sock;
$sock->send($request);
my @handles=IO::Select->new($sock)->can_read($timeout);
if (scalar(@handles) > 0)
{
$sock->recv($result,1024);
print "answer from zabbix server $zabbixserver: $result\n";
}
else
{
print "no answer from zabbix server\n";
}
$sock->close();
#!/bin/bash
array=( $(/��������/uniups /dev/ttyS0) )
Names=( InVolt FaultVolt OutVolt Current Freq UBatt UTemp NA )
correct[5]="24.0"
j=0;
echo "Checking UPS on serial A - ${#array[@]}"
if [ ${#array[@]} -gt "7" ]
then
param=""
for i in "${array[@]}"
do
if [[ ${correct[$j]} ]]
then
param=$( echo "scale = 0; $i * ${correct[$j]}" | bc)
else.
param=$i
fi
/��������/zabbix_sender.pl 192.168.53.23 KraulerMemoRT2000 ${Names[$j]} $param
j=$j+1;
done
else
echo "No data on A"
fi
Небольшие комментарии:
array — массив, который возвращает программа опроса ИБП, Names — массив с именами Item, на Zabbix сервере должны быть заведены элементы с такими же именами. KraulerMemoRT2000 — имя бесперебойника, должно совпадать с именем хоста на сервере.
возник баг, последнее возвращаемое скриптом значение, воспринимается сервером Zabbix как некорректное, независимо, причину не нашел, просто добавил элемент NA, который в принципе отсылается в никуда, на сервере его заводить не надо, после чего все стало нормально.
производится проверка на количество элементов массива ${#array[@]}, возвращаемых при опросе ИБП. Эта проверка отсекает ошибки при чтении с СОМ порта, в том числе когда ИБП выключен. В последнем случае до патча возникала интересная ошибка: Первый в массиве элемент данных отсылался с пустым значением, соответственно на стороне сервера возникала ошибка данных, а триггер по этому элементу не срабатывал. И что самое печальное, что данные числились полученными, т.е мониторинг выводил полученные данные под видом актуальных, в то время, как ИБП был отключен.
correct[5]=«24.0» это коррекция напряжения для батарей (5ый элемент в массиве), в том случае если ИБП возвращает напряжение на элементе (battery voltage/cell). У меня 6 элементов в батарее, 4 шт последовательно, итого 24. В принципе это описано в протоколе. Я посчитал лишним создавать отдельный элемент, так как все ИБП у меня мониторятся по шаблону и у всех 48 вольт. При мониторинге разновольтовых ИБП, конечно, надо будет несколько поменять структуру, возможно оптимальным будет забить параметры батарей на сервере.
Вышеописанный скрипт добавлен в cron на ежеминутное выполнение. Настройка заббикса в данной статье не рассматривается.
https://habr.com/ru/articles/397637/