Neben den lokalen Checks gibt es noch Checks die als Plugin im Agenten laufen. Der Unterschied liegt darin das auf dem Host auf dem der Agent läuft keine Bewertung der Informationen stattfindet, diese werden dem Check_MK Monitoring nur als Text zur Verfügung gestellt. Somit basiert ein Agent Plugin aus einem Host Teil (Agent Plugin) und einem Monitoring Server Teil (Check_MK Check).
Alle Dateien des DEMOs als MKP zum Download:
Check_MK Agent Check - My TCP Listener DEMO
Das Agent Plugin
Hierzu ein Beispiel:
Ein Check der die aktuellen offen TCP Listener Ports ausgibt. Die Scriptsprache ist egal, die Datei muss executeable sein.
my_tcp_listener.sh Script in /usr/lib/check_mk_agent/plugins
#!/bin/bash echo "<<<my_tcp_listener>>>" netstat -tlpen | sed 1,2d
Im <<<>>> Block wird der Checkname angegeben.
Folgende Ausgabe erzeugt das Plugin:
mmm@home:/home/mmm$ /usr/lib/check_mk_agent/plugins/test.sh <<<my_tcp_listener>>> tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 0 12608 621/sshd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 0 14982 1034/exim4 tcp 0 0 0.0.0.0:6556 0.0.0.0:* LISTEN 0 14882 793/xinetd tcp 0 0 127.0.0.1:6942 0.0.0.0:* LISTEN 1000 869419 15640/java tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN 999 13258 1230/apache2 tcp 0 0 127.0.0.1:63342 0.0.0.0:* LISTEN 1000 867186 15640/java tcp 0 0 0.0.0.0:34607 0.0.0.0:* LISTEN 105 1941 599/rpc.statd tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 0 1882 590/rpcbind tcp6 0 0 :::22 :::* LISTEN 0 12610 621/sshd tcp6 0 0 :::39352 :::* LISTEN 105 1945 599/rpc.statd tcp6 0 0 ::1:25 :::* LISTEN 0 14983 1034/exim4 tcp6 0 0 :::111 :::* LISTEN 0 1885 590/rpcbind tcp6 0 0 :::80 :::* LISTEN 0 13641 1006/apache2
Die Ausgabe wird wenn der Agent vom Server abgefragt wird angehängt. Das kann man selbst testen mit einem „telnet <host> 6556“.
In der Check Instanz auf dem Server muss nun noch ein Check geschrieben werden der die Ausgabe des Agenten verarbeiten kann.
Die eigenen Check Plugins auf dem Monitoring Server liegen unter /omd/sites/<sitename>/local/share/check_mk/checks
Beispiel zu my_tcp_listener, die Scriptsprache ist hier zwingend Python:
#!/usr/bin/python def inventory_my_tcp_listener(info): for l in info: service_identify = "%s/%s" % (l[0], l[3].split(":")[-1]) yield service_identify, None def check_my_tcp_listener(item, params, info): proto, port = item.split("/") message = (2, "CRIT service not listen") for l in info: if proto in l[0] and port in l[3].split(":")[-1]: message = (0, "OK service listen") return message check_info["my_tcp_listener"] = { "inventory_function" : inventory_my_tcp_listener, "check_function" : check_my_tcp_listener, "service_description" : "TCP Listener %s", }
Die Bestandteile des Check_MK Server Plugins
Inventory Funktion
Die Inventory Funktion dient dazu das WATO neue Services automatisch anlegen kann. Der info Variable wird die Ausgabe des Host Plugins übergeben. Dabei wird jede Zeile in eine Python Liste umgewandelt.
Eine Debugging Ausgabe ist möglich in dem man in die Inventory Funktion direkt ein print info einbaut. Check_MK stellt auch Pretty Print aus Python bereit, das Modul muss nicht importiert werden.
print info:
OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost Discovering services on localhost: localhost: [[u'tcp', u'0', u'0', u'0.0.0.0:22', u'0.0.0.0:*', u'LISTEN', u'0', u'12608', u'621/sshd'], [u'tcp', u'0', u'0', u'127.0.0.1:25', u'0.0.0.0:*', u'LISTEN', u'0', u'14982', u'1034/exim4'], [u'tcp', u'0', u'0', u'0.0.0.0:6556', u'0.0.0.0:*', u'LISTEN', u'0', u'14882', u'793/xinetd'], [u'tcp', u'0', u'0', u'127.0.0.1:6942', u'0.0.0.0:*', u'LISTEN', u'1000', u'869419', u'15640/java'], [u'tcp', u'0', u'0', u'127.0.0.1:5000', u'0.0.0.0:*', u'LISTEN', u'999', u'13258', u'1230/apache2'], [u'tcp', u'0', u'0', u'127.0.0.1:63342', u'0.0.0.0:*', u'LISTEN', u'1000', u'867186', u'15640/java'], [u'tcp', u'0', u'0', u'0.0.0.0:34607', u'0.0.0.0:*', u'LISTEN', u'105', u'1941', u'599/rpc.statd'], [u'tcp', u'0', u'0', u'0.0.0.0:111', u'0.0.0.0:*', u'LISTEN', u'0', u'1882', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::22', u':::*', u'LISTEN', u'0', u'12610', u'621/sshd'], [u'tcp6', u'0', u'0', u':::39352', u':::*', u'LISTEN', u'105', u'1945', u'599/rpc.statd'], [u'tcp6', u'0', u'0', u'::1:25', u':::*', u'LISTEN', u'0', u'14983', u'1034/exim4'], [u'tcp6', u'0', u'0', u':::111', u':::*', u'LISTEN', u'0', u'1885', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::80', u':::*', u'LISTEN', u'0', u'13641', u'1006/apache2']]
oder mit pprint.pprint(info)
OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost Discovering services on localhost: localhost: [[u'tcp', u'0', u'0', u'0.0.0.0:22', u'0.0.0.0:*', u'LISTEN', u'0', u'12608', u'621/sshd'], [u'tcp', u'0', u'0', u'127.0.0.1:25', u'0.0.0.0:*', u'LISTEN', u'0', u'14982', u'1034/exim4'], [u'tcp', u'0', u'0', u'0.0.0.0:6556', u'0.0.0.0:*', u'LISTEN', u'0', u'14882', u'793/xinetd'], [u'tcp', u'0', u'0', u'127.0.0.1:6942', u'0.0.0.0:*', u'LISTEN', u'1000', u'869419', u'15640/java'], [u'tcp', u'0', u'0', u'127.0.0.1:5000', u'0.0.0.0:*', u'LISTEN', u'999', u'13258', u'1230/apache2'], [u'tcp', u'0', u'0', u'127.0.0.1:63342', u'0.0.0.0:*', u'LISTEN', u'1000', u'867186', u'15640/java'], [u'tcp', u'0', u'0', u'0.0.0.0:34607', u'0.0.0.0:*', u'LISTEN', u'105', u'1941', u'599/rpc.statd'], [u'tcp', u'0', u'0', u'0.0.0.0:111', u'0.0.0.0:*', u'LISTEN', u'0', u'1882', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::22', u':::*', u'LISTEN', u'0', u'12610', u'621/sshd'], [u'tcp6', u'0', u'0', u':::39352', u':::*', u'LISTEN', u'105', u'1945', u'599/rpc.statd'], [u'tcp6', u'0', u'0', u'::1:25', u':::*', u'LISTEN', u'0', u'14983', u'1034/exim4'], [u'tcp6', u'0', u'0', u':::111', u':::*', u'LISTEN', u'0', u'1885', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::80', u':::*', u'LISTEN', u'0', u'13641', u'1006/apache2']]
Im Beispiel wird jetzt jede Zeile durchgegangen und ein eindeutiger Identifier (Servicename) erzeugt für den Service.
for l in info: service_identify = "%s/%s" % (l[0], l[3].split(":")[-1]) yield service_identify, None
Die Rückgabe der Funktion erfolgt über die Generator Funktion yield und erwartet ein Tuple aus 2 Werten. 1 Wert ist der Servicename, 2 Wert sind Parameter die als Tuple oder Dictionary übergeben werden können. In dem Beispiel haben wir keine Parameter somit übergeben wir ein None.
Hat man seine Inventory Funktion und die check_info Definition in seinem Check komplettiert kann man das auf der Kommandozeile testen:
OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost Discovering services on localhost: localhost: 1 cpu.loads 1 cpu.threads 1 df 1 diskstat 3 kernel 1 kernel.util 1 livestatus_status 1 lnx_if 3 local 1 mem.linux 1 mkeventd_status 1 mknotifyd 1 mounts 3 mtr 13 my_tcp_listener 1 ntp.time 1 omd_apache 1 omd_status 1 tcp_conn_stats 1 uptime
Die Inventory Funktion hat meine 13 lokalen TCP Ports gefunden und dem Host hinzugefügt. Gespeichert werden diese Informationen an folgendem Ort:
/omd/sites/<sitename>/var/check_mk/autochecks/<hostname>.mk
OMD[test1]:~/var/check_mk/autochecks$ cat localhost.mk [ ('cpu.loads', None, cpuload_default_levels), ('cpu.threads', None, threads_default_levels), ('df', u'/', {}), ('diskstat', u'SUMMARY', diskstat_default_levels), ('kernel', u'Context Switches', kernel_default_levels), ('kernel', u'Major Page Faults', kernel_default_levels), ('kernel', u'Process Creations', kernel_default_levels), ('kernel.util', None, {}), ('livestatus_status', u'test1', {}), ('lnx_if', u'2', {'state': ['1'], 'speed': 1000000000}), ('mem.linux', None, {}), ('mkeventd_status', u'test1', {}), ('mknotifyd', u'test1', {}), ('mounts', u'/', [u'data=ordered', u'errors=remount-ro', u'relatime', u'rw']), ('my_tcp_listener', u'tcp/111', None), ('my_tcp_listener', u'tcp/22', None), ('my_tcp_listener', u'tcp/25', None), ('my_tcp_listener', u'tcp/34607', None), ('my_tcp_listener', u'tcp/5000', None), ('my_tcp_listener', u'tcp/63342', None), ('my_tcp_listener', u'tcp/6556', None), ('my_tcp_listener', u'tcp/6942', None), ('my_tcp_listener', u'tcp6/111', None), ('my_tcp_listener', u'tcp6/22', None), ('my_tcp_listener', u'tcp6/25', None), ('my_tcp_listener', u'tcp6/39352', None), ('my_tcp_listener', u'tcp6/80', None), ('ntp.time', None, {}), ('omd_apache', u'test1', None), ('omd_status', u'test1', None), ('tcp_conn_stats', None, tcp_conn_stats_default_levels), ('uptime', None, {}), ]
Check Funktion
Die Check Funktion beinhaltet die Logik welche überprüft in welchem Zustand sich ein Service befindet (0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN).
Der Check Funktion werden 3 Variablen übergeben: item, params, info
Variable item
Der Variable wird der Servicename übergeben
Variable params
Hier werden die Parameter übergeben die beim Inventory oder durch eine WATO Regel für den Service gesetzt sind.
Variable info
Hier wird die komplette Ausgabe des Agent Check Plugins als Liste übergeben so wie bei der Inventory Funktion.
Die Check Funktion ist hier stark vereinfacht, grundsätzlich muss man sein item aus der Datenmenge info herraussuchen:
Beispiel aus einem anderen Check:
for line in info: if item == line[0]: # Hier der Check
Hier für unser Beispiel:
def check_my_tcp_listener(item, params, info): proto, port = item.split("/") message = (2, "CRIT service not listen") for l in info: if proto in l[0] and port in l[3].split(":")[-1]: message = (0, "OK service listen") return message
Als Rückgabe erwartet Check_MK aus 2 bzw. 3 Werten in einem Tuple. 2 Werte wenn keine Performance Daten übergeben werden, 3 wenn Performance Daten dabei sind.
ohne Performance Werte
<STATUS>, <MESSAGE>
mit Performance Werte
<STATUS>, <MESSAGE>, <PERFDATA>
Die Perfomance Werte sind wiederum eine Liste aus Tupeln. Das werde ich in einem anderen Artikel erläutern.
Das check_info Dictionary
Das check_info Dictionary ist ein Verzeichnis das von Check_MK selbst bereitgestellt wird und alle zur Verfügung stehenden Checks beinhaltet.
check_info["my_tcp_listener"] = { "inventory_function" : inventory_my_tcp_listener, "check_function" : check_my_tcp_listener, "service_description" : "TCP Listener %s", }
Der Key muss dem Namen entsprechen welche der Agent für die Sektion z.B. <<<my_tcp_listener>>> verwendet. Im Dictionary werden dann Check_MK die Inventory Funktion und die Check Funktion mitgeteilt. Desweiteren kann man hier den Text definieren welcher für den Service angezeigt wird. Hier wird z.B. für SNMP Checks die OIDs definiert. Mehr dazu in einem anderen Artikel …
Wenn der Check fertig ist muss der Check_MK Core neu geladen werden damit er den Check kennt, das erledigt folgendes Kommando in der OMD Umgebung „cmk -R“
Ob der Check funktioniert kann man mit „cmk -D localhost“ in der OMD Umgebung testen.
OMD[test1]:~/local/share/check_mk/checks$ cmk -v -D localhost localhost Addresses: 127.0.0.1 Tags: /wato/, cmk-agent, ip-v4, ip-v4-only, lan, prod, site:test1, tcp, wato Host groups: Contact groups: all Type of agent: TCP (port: 6556) Is aggregated: no Services: checktype item params description groups summarized to groups ----------------- ------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------ ------ ------------- ------ ... my_tcp_listener tcp/111 None TCP Listener tcp/111 my_tcp_listener tcp/22 None TCP Listener tcp/22 my_tcp_listener tcp/25 None TCP Listener tcp/25 my_tcp_listener tcp/34607 None TCP Listener tcp/34607 my_tcp_listener tcp/5000 None TCP Listener tcp/5000 my_tcp_listener tcp/63342 None TCP Listener tcp/63342 my_tcp_listener tcp/6556 None TCP Listener tcp/6556 my_tcp_listener tcp/6942 None TCP Listener tcp/6942 my_tcp_listener tcp6/111 None TCP Listener tcp6/111 my_tcp_listener tcp6/22 None TCP Listener tcp6/22 my_tcp_listener tcp6/25 None TCP Listener tcp6/25 my_tcp_listener tcp6/39352 None TCP Listener tcp6/39352 my_tcp_listener tcp6/80 None TCP Listener tcp6/80 .... OMD[test1]:~/local/share/check_mk/checks$
Debugging Plugin
Per default werden Exceptions auf der Kommandozeile unterdrückt. Man kann mit –debug die Ausgaben beim „cmk“ Kommando einschalten.
OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II --debug localhost Discovering services on localhost: localhost: Traceback (most recent call last): File "/omd/sites/test1/share/check_mk/modules/check_mk.py", line 4838, in <module> do_discovery(hostnames, check_types, seen_I == 1) File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 80, in do_discovery do_discovery_for(hostname, check_types, only_new, use_caches, on_error) File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 100, in do_discovery_for new_items = discover_services(hostname, check_types, use_caches, do_snmp_scan, on_error) File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 718, in discover_services return services + discover_services_impl(hostname, check_types, use_caches, on_error, ipaddress) File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 733, in discover_services_impl use_caches, on_error, use_snmp): File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 899, in discover_check_type discovered_items = list(discovered_items) File "/omd/sites/test1/local/share/check_mk/checks/my_tcp_listener", line 6, in inventory_my_tcp_listener yield service_identify, None, xxxx,xxxx,xx NameError: global name 'xxxx' is not defined
Fertig ???
Im WATO muss man seinen neuen Check auf dem Host noch mit einem einfachen „Tabula Rasa“ inventarisieren.
Jetzt kann man sein Ergebnis auf der Webseite bewundern 🙂
1 Gedanke zu „Check_MK: Eigener Agent Check“