Nagios, gammu i Huawei 1820 (USB)

Autor , 23.09.2011 10:00

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.

Skomentuj wpis

Panorama Theme by Themocracy