Mit Raspberry Pi und Livestatus eine Nagiosampel steuern
20. September 2012 | Von Thorsten Robers | Kategorie: Monitoring | Availability | StickyBitEine schöne Idee den aktuellen Status der verwendeten Systeme, die sowieso durch Nagios überwacht werden, aufzubereiten und auf die eine oder andere Art darzustellen ist eine Nagiosampel. Vor einigen Jahren haben wir unsere eigene Ampel in Betrieb genommen. Da mittlerweile technisch deutlich effizientere Methoden zur Abfrage des Nagios-Stati vorhanden sind war eine Überarbeitung längst überfällig. Als dann mit Fabian Ising ein Praktikant bei uns war hat dieser sich mit der Ampel und deren Ansteuerung genauer beschäftigt.
Im Folgenden wird gezeigt, wie mit Hilfe eines Raspberry Pi über WLAN (hierzu muss ein separater WLAN Stick verwendet werden) der Livestatus-Socket der Nagios-Core abgefragt und entsprechend die Ampel über GPIO Pins geschaltet werden kann. Dieser Artikel basiert auf der Dokumentation von Fabian.
Installation des Raspberry Pi und Einrichten des WLANs
Auf dem Raspberry Pi kommt die speziell angepasste Distribution Raspbian, die wiederum auf Debian Wheezy basiert, zum Einsatz. Für den konkreten Aufbau wurde das Image 2012-08-16-wheezy-raspbian.zip verwendet. Für das Netzwerk ist eine WLAN-Verbindung erforderlich, da die Ampel im Aufenthaltsraum an der Wand hängt und weit und breit kein LAN-Anschluss verfügbar ist. Beim Anschluss des WLAN-Sticks muss beachtet werden, dass der Raspberry Pi genug Strom durch das Netzteil erhält, da die Stromversorgung des USB-WLAN-Stick über das Board erfolgt.
Zunächst wurde geschaut welchen Chipsatz der WLAN-Stick verwendet und die entsprechende Firmware zu installiert.
$ aptitude install zd1211-firmware
Nach dem Aus- und wieder Einstecken des WLAN-Stick sollten Kernelmodule in der Ausgabe von lsmod bzw. der WLAN-Stick selbst in der Ausgabe von dmesg erscheinen. Um sich zu einem WPA verschlüsselten Netzwerk zu verbinden muss das Paket wpasupplicant installiert und die WLAN Konfiguration in /etc/network/interfaces angepasst werden
auto wlan0 iface wlan0 inet dhcp wpa-ssid "Ampel" wpa-psk "f1bsc3b0f..."
Nach diesen Anpassungen sollte der Raspberry Pi automatisch eine verschlüsselte WLAN-Verbindung zum Router aufbauen können. Eventuell kann hier ein
$ ifdown wlan0 $ ifup wlan0
den Verbindungsaufbau explizit anstossen.
Die Kommunikation mit dem Livestatus-Socket
Für die Abfrage der benötigten Informationen wurde bisher auf eine Datenbank zurückgegriffen in die der Nagios-Core mit den NDO-Utils alle Check-Ergebnisse geschrieben hat. Da die Datenbank ansonsten nicht weiter benötigt wurde verursachte dies einen überflüssigen administrativen Aufwand und stellt auch einen unnötigen Ressourcenverbrauch dar. Eine deutlich effizientere Variante ist eine Abfrage mittels Livestatus-Socket, der eine direkte netzwerkbasierte Kommunikation mit dem Nagios-Core-Prozess ermöglicht.
Das verwendete Shell Script testet alle Services und Hosts auf unbearbeitete Warnungen und Fehler. Sollte es unbearbeitete Fehler geben so soll die Ampel rot leuchten. Hierzu wird dem Output-Skript (ioport.sh) eine 1 übergeben. Bei einer unbearbeiteten Warnung soll die orange Lampe aufleuchten, wofür eine 2 übergeben wird. Sollte ein unbearbeiteter unbekannter Fehler vorhanden sein so soll sowohl die rote als auch die orange Lampe aufleuchten, hierzu wird eine 3 übergeben. Sind keine unbearbeiteten Probleme vorhanden, so soll die Ampel grün aufleuchten. Für den letzten Fall wird eine 4 übergeben. Sollte es generelle Probleme bei der Abfrage geben so sollen alle drei Lampen aufleuchten. Hierzu wird an das Output-Skript eine 7 übergeben.
Hier zunächst das eigentliche Bash-Skript zur Abfrage des Nagios-Core über den Livestatus-Socket:
#!/bin/bash nagiosip="x.x.x.x"; IFS=";" SUDO="/usr/bin/sudo" IOPORT="/home/pi/ampel/ioport.sh"; services=$(netcat $nagiosip 6557 < /home/pi/ampel/services); set -- $services critical=$1; warning=$2; unknown=$3; # Tritt nur bei fehlender Verbindung auf if [ "$1" = "" ] || [ "$2" = "" ] || [ "$2" = "" ] then #Ampel rot/gelb/gruen $SUDO $IOPORT 7; else host=$(netcat $nagiosip 6557 < /home/pi/ampel/hosts); IFS=";" set -- $host if [ "$1" = "" ] || [ "$2" = "" ] || [ "$2" = "" ] then #Ampel rot/gelb/gruen $SUDO $IOPORT 7; else critical=`expr $critical + $1` warning=`expr $warning + $2` unknown=`expr $unknown + $3` if [ $critical -gt 0 ] then #Ampel rot, kritisch $SUDO $IOPORT 1; elif [ $warning -gt 0 ] then #Ampel gelb, Warnung $SUDO $IOPORT 2 elif [ $unknown -gt 0 ] then #Ampel gelb/rot, Unbekannter Fehler $SUDO $IOPORT 3 elif [ "$warning" = "" ] || [ "$critical" = "" ] || [ "$unknown" = "" ] then #Ampel rot/gelb/gruen: Auslesen fehlgeschlagen $SUDO $IOPORT 7; else #Ampel gruen, alles in Ordnung $SUDO $IOPORT 4; fi fi fi exit 0
Dieser Skript wird als Cronjob jede Minute ausgeführt und fragt die entsprechenden Daten beim Nagios-Core an. Welche Daten angefragt werden erläutert der folgende Ausschnitte, die als Datei services bei der Verarbeitung des Skriptes eingelesen wird.
GET services Stats: state = 2 Stats: acknowledged = 0 StatsAnd: 2 Stats: state = 1 Stats: acknowledged = 0 StatsAnd: 2 Stats: state = 3 Stats: acknowledged = 0 StatsAnd: 2
Analog zu den services fragt das Skript auch die hosts über den Livestatus-Socket ab. Hierzu ist lediglich das servives durch hosts zu ersetzen und als Datei mit der Bezeichnung hosts abzuspeichern. Für die Abfrage des Livestatus-Socket wird eine Query Language eingesetzt, die eine logisch Verknüpfung von Abfragen ermöglicht. Eine genauere Erläuterung finden Sie auf den oben verlinkten Seiten zum Livestatus.
Wichtig ist, dass sowohl die services– als auch die hosts-Datei am Ende eine Leerzeile enthalten müssen, da diese Zeile vom Livestatus benötigt wird. Hieran erkennt Livestatus das Ende einer Anfrage und führt diese erst dann aus, andernalls wird der Livestatus die Verbindung einfach zurücksetzen. Es ist außerdem noch zu beachten, dass die IP-Adresse, mit der der Raspberry Pi auf das Nagios zugreift, in der xinetd-Konfiguration des Livestatus Sockets erlaubt sein muss.
Eine Ausgabe auf die Ampel – das Output-Skript
Die Ampel wird mit einer Relaiskarte geschaltet, die in unser bisherigen Lösung über den Parallel Port eines Laptops gesteuert wurde. Da der Raspberry Pi jedoch über keine parallele Schnittstelle verfügt, werden hierfür die GPIO Pins des Boards verwendet. Diese können relativ einfach als Dateien angesprochen werden:
$ echo "17" > /sys/class/gpio/export $ echo "out" > /sys/class/gpio/gpio17/direction $ echo "1" > /sys/class/gpio/gpio17/value
Durch die vorhergehenden Befehle wird zuerst der GPIO Pin 17 (Belegung) zur weiteren Verwendung freigegeben, dann der selbe Pin als Output gesetzt und dann wird der Pin aktiviert, wobei standardmäßig bei aktiviertem Pin ein relativ konstanter Spannungspegel von 3,3V anliegt. Soll eine Pin wieder deaktiviert werden wird einfach
$ echo "0" > /sys/class/gpio/gpio17/value
ausgeführt. Sollen die Pins auch wieder freigegeben werden muss noch
$ echo "17" > /sys/class/gpio/unexport
ausgeführt werden. In unserem Szenario benutzen wir die Pins 17 für die rote, 22 für die orange und 23 für die grüne Ampelleuchte. Damit zur Schonung der Birnen möglichst wenig Schaltvorgänge stattfinden werden immer nur die Lampen ausgeschaltet, die nicht genutzt werden. Das folgende Skript ist das in unserem Falle verwendete Output-Skript ioport.sh:
#!/bin/bash GPIO="/sys/class/gpio"; if [ ! -L "/sys/class/gpio/gpio17" ]; then echo "17" > $GPIO/export; echo "out" > $GPIO/gpio17/direction; fi if [ ! -L $GPIO/gpio22 ]; then echo "22" > $GPIO/export; echo "out" > $GPIO/gpio22/direction; fi if [ ! -L $GPIO/gpio23 ]; then echo "23" > $GPIO/export; echo "out" > $GPIO/gpio23/direction; fi if [ $1 -eq 1 ] then echo "0" > $GPIO/gpio22/value; echo "0" > $GPIO/gpio23/value; echo "1" > $GPIO/gpio17/value; fi if [ $1 -eq 2 ] then echo "0" > $GPIO/gpio17/value; echo "0" > $GPIO/gpio23/value; echo "1" > $GPIO/gpio22/value; fi if [ $1 -eq 3 ] then echo "0" > $GPIO/gpio23/value; echo "1" > $GPIO/gpio17/value; echo "1" > $GPIO/gpio22/value; fi if [ $1 -eq 4 ] then echo "0" > $GPIO/gpio17/value; echo "0" > $GPIO/gpio22/value; echo "1" > $GPIO/gpio23/value; fi if [ $1 -eq 7 ] then echo "1" > $GPIO/gpio17/value; echo "1" > $GPIO/gpio22/value; echo "1" > $GPIO/gpio23/value; fi