Zabbix sender per Web URL-Aufruf nutzen
Aus znilwiki
Vorwort
In letzter Zeit beschäftige ich mich viel mit den ESP8266 Modulen. Bisher habe ich z.B. einen WLAN-Taster und eine Temperatur/Luftfeuchtigkeits-Messtation gebaut.
Die erfassten Werte (unter anderem der Batteriespannung um zu wissen wann ich nachladen muss) möchte ich natürlich in Zabbix haben.
Nun gibt es zwar schon einige Versuche einen "Zabbix-Agenten" auf dem ESP zu simulieren - aber das ist mir alles viel zu umständlich.
Ich nutze Python und Flask schon eine Weile um darüber meine 32 Relais an einem Raspberry Pi zu bedienen.
Stark vereinfacht erklärt: Flask erstellt einen kleinen Webserver der die aufgerufene URL als Variablen an das Pythonskript übergibt.
Vorbereitung Server
Ich lasse das Python-Skript das den Aufruf entgegen nimmt in diesem Beispiel mit auf dem Zabbix-Server laufen.
Es läuft auf Port 5000 und kommt somit der Zabbix-Webseite auf Port 80/443 nicht in die Quere.
Basis ist bei mir eine Zabbix 4.0.x Server unter Ubuntu 18.04. LTS. Es sollte aber auch genauso auf älteren Systemen laufen.
Meldet euch an eurem Server an und wechselt zum root-Benutzer.
Zunächst prüfen wir ob Python schon installiert ist:
root@zabbix:~# python --version Python 2.7.15rc1
Was eigentlich der Fall sein sollte. Es gibt auch noch python3 was hier erst einmal egal ist. Das ganze hier läuft sowohl unter Python 2.7 als auch 3.6
Jetzt installieren wir Flask nach
apt install python-flask -y
Skript Stufe 1: Der Test
In Stufe 1 testen wir nun den Zugriff. Dazu erstellen wir unser Skript wie folgt:
mkdir /opt/flask nano /opt/flask/ZabbixSenderByURL.py
und fügt folgenden Inhalt ein:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
from functools import wraps
from flask import Flask, request, Response
app = Flask(__name__)
# Wenn nur die URL ohne Parameter aufgerufen wird
@app.route('/')
def hello():
return "Start ZabbixSenderByURL! Use /ZabbixServer/Hostname/Item/Value"
# Wenn die URL mit Parametern aufgerufen wird
@app.route('/<zabbixserver>/<zhostname>/<zitem>/<zvalue>')
def ZabbixSender(zabbixserver, zhostname, zitem, zvalue):
print("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue)
#os.system("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue + " >/dev/null 2>&1")
return ('OK'), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Das Programm wird uns nun nur den zabbix_sender Aufruf anzeigen, diesen aber noch nicht ausführen.
Achtet darauf das die Einrückung exakt 4 Zeichen betragen!
Wir machen es ausführbar:
chmod +x /opt/flask/ZabbixSenderByURL.py
und starten es:
/opt/flask/ZabbixSenderByURL.py
Ausgabe:
root@zabbix:~# /opt/flask/ZabbixSenderByURL.py * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
Nun rufen wir ein einem Webbrowser die folgende URL auf (IP an euren Server anpassen):
http://10.100.12.15:5000/zabbix.znil.local/Messstation01/znil.esp8266.DHT22.temperature/23
welcher uns dann ein schnödes "OK" zurück gibt.
Die Ausgabe in der Bash sollte nun wie folgt aussehen:
root@zabbix:~# /opt/flask/ZabbixSenderByURL.py * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) /usr/bin/zabbix_sender -z zabbix.znil.local -s Messstation01 -k znil.esp8266.DHT22.temparture -o 23 172.16.103.2 - - [27/Feb/2019 10:03:14] "GET /zabbix.znil.local/Messstation01/znil.esp8266.DHT22.temperature/23 HTTP/1.1" 200 - 172.16.103.2 - - [27/Feb/2019 10:03:14] "GET /favicon.ico HTTP/1.1" 404 -
Das war es schon ... funktioniert. In den letzten beiden Zeilen sehen wir das der Aufruf der URL mit "200" quittiert wurde.
Der Browser wollte dann noch ein favicon.ico haben was wir aber nicht geliefert haben - das ignorieren wir.
Skript Stufe 2: Scharfschalten und als Dienst
Drückt in der Bash STRG + C um den Server wieder zu beenden.
Bearbeitet das Skript noch einmal:
nano /opt/flask/ZabbixSenderByURL.py
und ändert die folgenden 2 Zeilen:
#print("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue)
os.system("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue + " >/dev/null 2>&1")
Nun wird der Zabbix-Sender ausgeführt wenn die URL aufgerufen wird.
Jetzt richten wir das als Dienst ein:
nano /etc/systemd/system/zabbixsenderbyurl.service
Inhalt:
[Unit] Description=Zabbix Sender URL Service After=syslog.target [Service] Type=simple WorkingDirectory=/opt/flask ExecStart=/opt/flask/ZabbixSenderByURL.py SyslogIdentifier=zabbixsenderbyurl StandardOutput=syslog StandardError=syslog Restart=always RestartSec=3 [Install] WantedBy=multi-user.target
Automatischen Start einschalten:
systemctl enable zabbixsenderbyurl.service
und starten:
systemctl start zabbixsenderbyurl.service
Das Item
Das Item zu dem Beispiel
http://10.100.12.15:5000/zabbix.znil.local/Messstation01/znil.esp8266.DHT22.temperature/23
sieht wie folgt aus:
Wenn wir nun die URL noch einmal aufrufen:
Erklärung
Um mit dem Programm zabbix_sender Daten zu senden nutzen wir folgenden Aufruf:
zabbix_sender -z <zabbixserver> -s <host> -k <item> -o <wert> zabbix_sender Das Programm zum Werte senden -z <zabbixserver> Name oder IP des Zabbix-Servers oder Zabbix-Proxy, z.B. 127.0.0.1 -s <host> Name des Host in Zabbix, z.B. Messstation01 -k <item> Name des Items das den Wert bekommen soll, z.B. znil.esp8266.DHT22.temperature -o <wert> Wert der gesendet werden soll, z.B. 23
Der ganze Aufruf wäre also z.B.
zabbix_sender -z 127.0.0.1 -s Messstation01 -k znil.esp8266.DHT22.temperature -o 23
Den Aufruf verpacken wir nun in einen URL-Aufruf wie wir Ihn in jedem Browser nutzen können:
http://10.100.12.15:5000/zabbix.znil.local/Messstation01/znil.esp8266.DHT22.temperature/23
Das Python-Skript bekommt so die Werte und sendet diese einfach per Zabbix-Sender weiter.
Anpassungen
Port ändern
Das Skript läuft auf Port 5000. Umd as zu ändern müsst Ihr die letzte Zeile im Skript ändern:
app.run(host='0.0.0.0', port=5000)
Und dort die gewünschte Nummer angeben.
URL mit Benutzername + Passwort absichern
Man kann eine "Basic-Authentication" davorschalten - dann poppt beim Aufruf ein Fenster für Benutzername + Passwort auf.
Man kann die Anmeldung aber auch in der URL übergeben - was es einfacher macht.
Ändert das Skript wie folgt ab:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
from functools import wraps
from flask import Flask, request, Response
app = Flask(__name__)
# Wenn nur die URL ohne Parameter aufgerufen wird
@app.route('/')
def hello():
return "Start ZabbixSenderByURL! Use /ZabbixServer/Hostname/Item/Value"
#nur nach Anmeldung, siehe http://flask.pocoo.org/snippets/8/
def check_auth(username, password):
# This function is called to check if a username /
# password combination is valid.
return username == 'zabbix' and password == 'xibbaz'
def authenticate():
# Sends a 401 response that enables basic auth
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
# Wenn die URL mit Parametern aufgerufen wird
@app.route('/<zabbixserver>/<zhostname>/<zitem>/<zvalue>')
@requires_auth
def ZabbixSender(zabbixserver, zhostname, zitem, zvalue):
#print("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue)
os.system("/usr/bin/zabbix_sender -z " + zabbixserver + " -s " + zhostname + " -k " + zitem + " -o " + zvalue + " >/dev/null 2>&1")
return ('OK'), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Den Benutzernamen und das Passwort im Skript müsst Ihr anpassen.
Der Aufruf wäre nun:
http://zabbix:xibbaz@10.100.12.15:5000/zabbix.znil.local/Messstation01/znil.esp8266.DHT22.temperature/23
https nutzen
Immer gleich übertreiben ... im internen Netzwerk nutze ich das nicht. Wer das möchte den verweise ich auf folgende beiden Artikel:
- https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https
- http://flask.pocoo.org/snippets/111/