Nagios, gammu i Huawei 1820 (USB)
Od czasu do czasu walczę z systemami monitorującymi. Za każdym razem sytuacja wygląda idealnie do czasu gdy trzeba zacząć wysyłać powiadomienia SMS o awariach. Tutaj zawsze jest pod górę, zawsze coś jest ‘nie tak’. W 2006 roku zmagałem się z modemem Sony Ericsson GC89 (PCMCIA). Tym razem zderzyłem się z Huawei 1820 (USB).
Zabawa była dość prosta w zakresie instalacji i konfiguracji systemu (Debian 6), nagiosa (kompilowany, używam wersji 1.4 historycznie, oraz 3.2 jako docelowej) i gammu (paczkowane z dystrybucji). Pierwotne testy wypadały zadowalająco: SMS’y wychodziły, docierały i wszystko było w porządku. Po wdrożeniu na produkcji zonk! – powiadomienia SMS przestały działać.
Sprawdziłem wszystko co chciałem i mogłem. Modem wieszał się na sztywno, niedeterministycznie w przedziale od dwóch do ośmiu godzin, a próba jego reanimacji kończyła się każdorazowo komunikatem:
1 | Error opening device. Unknown, busy or no permissions. |
Jedynym rozwiązaniem było albo wypięcie i wpięcie modemu (gdy się było na miejscu), albo restart maszyny (gdy się przebywało poza firmą i zabrakło ‘zdalnej ręki’ kolegów z działu).
Problem z wysyłką powiadomień SMS okazał się na tyle skomplikowany, iż rozwiązanie go zajęło trochę czasu. Poniżej opisuję zmiany, które przyczyniły się do udrożnienia powiadomień.
1. Zamiana gammu na gammu-smsd.
Wymóg był oczywisty i wynikał z metodologii obsługi urządzeń USB (Huawei) w systemie. Ponadto Nagios musiał wysyłać powiadomienia w sposób pozwalający na przekazanie ich do odbiorców niezależnie od potencjalnych błędów modemu/systemu/demona – stąd przejście z gammu na gammu-smsd, którego mechanizmy kolejkowania zostały napisane naprawdę dobrze.
Tutaj uwaga: paczkowane gammu-smsd w Debianie 6 zawiera poważne błędy, które uniemożliwiają wysyłkę powiadomień w przypadku problemów z SMSC (czyli w moim przypadku: zawsze).
Na szczęście można obejść ten problem instalując paczki przygotowane dla Ubuntu. Wystarczy dodać poniższe repozytorium i zaktualizować system:
1 | deb http://ppa.launchpad.net/nijel/ppa/ubuntu natty main |
Dla leniwych podaję jeszcze zawartość pliku /etc/gammu-smsdrc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [gammu] port = /dev/ttyUSB0 connection = at [smsd] PIN = **** # podajemy PIN jeśli jest service = files logfile = /var/log/smsd.log maxretries = 2 debuglevel = 1 # commtimeout = 30 # kolejkowanie inboxpath = /var/spool/gammu/inbox/ outboxpath = /var/spool/gammu/outbox/ sentsmspath = /var/spool/gammu/sent/ errorsmspath = /var/spool/gammu/error/ |
2. Utrzymanie ciągłości działania modemu Huawei 1820 (USB).
Nie chcę opisywać ile dobrych rad wujka Google przetestowałem zanim doszedłem do rozwiązania, które sprawdza się na produkcji. Powiem, że było ich sporo, a moim głównym problemem okazały się zmiany wprowadzone w jądrze Linuksa od wersji 2.6.31, które praktycznie nieomal wykluczają modemy USB Huawei (i inne) z pracy ciągłej w systemie. Nieomal. Obejście problemu, które zastosowałem wygląda następująco:
2.1 Resetujemy podłączenie modemu USB.
Potrzebujemy do tego programu, który tworzymy sami w kilku krokach. Najpierw źródło (zapisać pod dowolną nazwą: u mnie reset.c):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /* usbreset -- send a USB port reset to a USB device */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
int main(int argc, char **argv)
{
const char *filename;
int fd;
int rc;
if (argc != 2) {
fprintf(stderr, "Usage: usbreset device-filename\n");
return 1;
}
filename = argv[1];
fd = open(filename, O_WRONLY);
if (fd < 0) {
perror("Error opening output file");
return 1;
}
printf("Resetting USB device %s\n", filename);
rc = ioctl(fd, USBDEVFS_RESET, 0);
if (rc < 0) {
perror("Error in ioctl");
return 1;
}
printf("Reset successful\n");
close(fd);
return 0;
} |
Następnie kompilacja i dodanie do systemu np. w /usr/local/sbin:
1 | cc reset.c -o /usr/local/sbin/usbreset |
Plikowi nadajemy prawa do wykonywania metodą ulubioną. Powstały tą metodą program restartuje urządzenia bazując na lokalizacji urządzenia. Metoda wywołania:
1 | /usr/local/sbin/usbreset /dev/bus/usb/001/002 |
- gdzie 001 i 002 pobieramy via lsusb co w moim przypadku oznaczało:
1 | Bus 001 Device 002: ID 12d1:14ac Huawei Technologies Co., Ltd. |
2.1 Dodajemy zadanie cykliczne.
W moim przypadku wywołuję cyklicznie, w odstępach godzinnych (minimalny czas pracy ciągłej modemu Huawei 1820 na produkcji oscyluje w granicach 150 minut, ale jestem nadgorliwy), taki oto skrypt (/usr/local/sbin/huawei.sh):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/bin/bash
# ścieżki - niezbędne
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# do rzeczy: najpierw zabijamy proces gammu-smsd
/etc/init.d/gammu-smsd stop
sleep 30
# upewniamy się, że umarł bo jeśli urządzenie padło w międzyczasie to umiera niechętnie
killall -9 gammu-smsd
sleep 20
# ustalamy na podstawie vendora i urzadzenia lokalizację
bus=$(lsusb -d 12d1:14ac | sed 's#^Bus\ \([0-9]\+\) Device \([0-9]\+\):\ ID.*#/dev/bus/usb/\1/\2#')
if [ -z "$bus" ]; then
echo "Nie znalazlem urzadzenia USB!"
echo ""
lsusb
exit 1
fi
# i wykonujemy reset urządzenia USB
/usr/local/sbin/usbreset $bus
sleep 10
# startujemy gammu smsd
/etc/init.d/gammu-smsd start
sleep 10
# i jeszcze sms testowy + dodatki -- do wywalenia na produkcji zostawiam dla potomnych
# echo "HUAWEI dziala" | gammu-smsd-inject TEXT +48******
# tail -f /var/log/smsd.log |
3. I jeszcze konfiguracja powiadomienia w Nagiosie.
W moim przypadku dodałem dwa wpisy w misccommand.cfg:
1 2 3 4 5 6 7 8 9 10 | # definicje SMS
define command {
command_name notify-host-by-sms
command_line /usr/bin/printf "%b" "$DATE$ $TIME$ $SERVICEDESC$ $HOSTNAME$ $HOSTADDRESS$ $SERVICESTATE$ $HOSTOUTPUT$" | gammu-smsd-inject TEXT "$CONTACTPAGER$"
}
define command {
command_name notify-service-by-sms
command_line /usr/bin/printf "%b" "$DATE$ $TIME$ $SERVICEDESC$ $HOSTNAME$ $HOSTADDRESS$ $SERVICESTATE$ $SERVICEOUTPUT$" | gammu-smsd-inject TEXT "$CONTACTPAGER$"
} |
- przy założeniu, że numer SMS odbiorcy deklarowany jest zmienną pager w define contact w deklaracjach kontaktów w Nagiosie oraz ustawiono metodę powiadamiania jako:
1 2 | host_notification_commands notify-host-by-sms service_notification_commands notify-service-by-sms |
I to by było na tyle. Szczególne podziękowania dla Piotra za wysłuchiwanie przez ostatnie pół roku moich, przydługich momentami, monologów na temat obsługi urządzeń USB w systemach Linuksowych, oraz uczynienie uniwersalnym skryptu do restartów modemu.
