Archiv der Kategorie: Monitoring / Check_MK

Check_MK: TG-Notify – Telegram Notification Bot with Callback for ACK & Downtime

TG-Notify ist eine von mir geschriebene Erweiterung für Check_MK welche Notifications über Telegram ermöglicht und über Callback Acknowledge und Downtimes setzen kann für Hosts/Services.

Sourcecode: https://github.com/lanbugs/tg-notify

Status/ Version: 0.1 beta

Download als MKP: folgt

Lizenz: GPLv2

Vorteile:

  • Telegram ist kostenlos
  • Telegram ist für alle gänigen Mobile und PC OS verfügbar -> https://telegram.org/apps
  • Telegram arbeitet mit einer HTTP API, unabhänig von E-Mail
  • Direkte Callbacks möglich, implementiert sind Acknowledge und Downtime -> Super für Bereitschaft wenn es nichts dringendes ist um den Schlaf nicht lange zu unterbrechen 🙂
  • Für die Callbacks muss Check_MK nicht über das Internet direkt erreichbar sein
  • mehr als 160 Zeichen möglich (vgl. SMS)
  • uvm.

Geplant / Future:

  • Eigene Ack / Downtime Texte
  • Weitere Komandos (Ideen ??)

Installation TG-Notify

TG-Notify ist in Python geschrieben und kommt mit den Standard Libraries von Python 2.7 aus die von Check_MK in der OMD Umgebung bereitgestellt werden.

Verwendete Python Libraries:

  • argparse
  • ConfigParser
  • sqlite3
  • os
  • sys
  • urllib
  • json
  • datetime
  • time
  • random
  • string

Bestandteile von TG-Notify:

  • tg_admin – Admin Tool welches auch die Callbacks einsammelt
  • tg_callback – Callback Tool welches die Notifications und Downtimes setzt
  • tg_runner – Cronjob Script
  • tg_notification_with_callback – Notification Script welches die Notifications aus Check_MK versendet
  • tg.ini – Configdatei von TG-Notify
  • tg.db – SQLite3 Datenbank welche mit dem tg_admin Tool generiert werden kann
  •  tg_collect_and_callback_agent, tg_cleanup – Cronjobs die ebenfalls mit dem tg_admin Tool generiert werden

Verzeichnisse -> Dateien:

  • OMD_ROOT/local/bin -> tg_admin und tg_callback, tg_runner
  • OMD_ROOT/local/etc -> tg.ini
  • OMD_ROOT/local/lib/tg_notify -> tg.db
  • OMD_ROOT/local/share/check_mk/notifications -> tg_notification_with_callback
  • OMD_ROOT/etc/cron.d -> tg_collect_and_callback_agent, tg_cleanup

Schritt 1: Anlegen eines Telegram Bots beim BotFather

Message an BotFather schicken mit „/newbot“, anschließend Namen definieren für neuen Bot z.B. „TGnotifyDEMO_bot“. Der BotFather schickt anschlieend den Token für die HTTP API von Telegram.

Schritt 2: Anlegen eines Automation Users in Check_MK

Damit die Callbacks ACKs und Downtimes setzen können muss ein Automation User eingerichtet werden. Der User muss als Contact Group die gleichen hanben wie die User sonst kann er keine ACKs/Downtimes setzen. Alternativ muss das Userprofil angepasst werden. Für den Automation User sind die Notifications zu deaktivieren!

Schritt 3: tg.ini anpassen

Hier ist der Token vom BotFather einzugeben und der Automation User sowie die URL zum Check_MK System.

;
; Configuration for  TG-Notify
;

[Telegram]
; URL mit /
url = https://api.telegram.org/
; Token with bot at the beginning, without / at the end
token = bot123456789:AAAAAbBbbbCCcCccDDddDdDEeEeEEEEeE-FFF

[Database]
; Relative to OMD_ROOT without / at the beginning
path = local/lib/tg_notify/
file = tg.db

[Check_MK]
url = http://localhost/dev1/check_mk/
automation_user = a_test
automation_pass = ATPQTVBKJYGSDUAHYDQF

Schritt 4: Datenbank generieren

Die Funktion ist in tg_admin eingebaut, dazu einfach „tg_admin –initialize-database“ aufrufen.

OMD[dev1]:~$ tg_admin --initialize-database

Schritt 5: Crontab generieren

Die Funktion ist in tg_admin eingebaut, dazu einfach „tg_admin –generate-cronjobs“ aufrufen.

OMD[dev1]:~$ tg_admin --create-cronjobs

Schritt 6: Neuen User anlegen in TG-Notify

TG-Notify hat seine eigene Userdatenbank, es müssen die Check_MK User <-> Chat_ID von Telegram TG-Notify erst bekannt gemacht werden.

Dazu vom Handy ein „open“ an den Bot senden und das Kommando „tg_admin –show-agent“ aufrufen. Es wird die entsprechende Chat_ID angezeigt die der User hat. Anschließend den User mit „tg_admin -c -u hdampf -i 123456789“ anlegen. Aktuelle User können mit „tg_admin -s“ abgefragt werden.

OMD[dev1]:~/local/etc$ tg_admin --show-agent
----------------------------------------------------------------
First Name: Hans
Last Name: Dampf
CHAT ID: 123456789
----------------------------------------------------------------

OMD[dev1]:~/local/etc$ tg_admin -c -u hdampf -i 319651791

OMD[dev1]:~/local/etc$ tg_admin -s
Current User:

Username    Chat_ID     
------------------------
hdampf      123456789   

Schritt 7: Neue Notification Rule erzeugen für TG-Notify

Nach der Installation von TG-Notify umbedingt einen „omd restart“ durchführen damit das neue Notifications Script gefunden wird von Check_MK. Die Regel wie gewohnt erstellen, hier im Beispiel ist es eine sehr einfach gehaltene, es sind alle bekannten Optionen möglich.

Fertig! Erste Gehversuche …

Ich habe in meinem Testsystem Hosts (10.44.4.5, 10.44.4.6) angelegt welche es nicht gibt um Meldungen zu generieren. Mit „tg_admin –show-notify-history“ kann man sich die letzten Notifications ansehen. Für jede Notification wird eine Callback_ID generiert. Die ID dient dazu wenn vom Telegram Client ein Callback aufgeführt wird z.B. Acknowledge eines Services den Callback eindeutig zu identifizieren.

Im Callback wird dann z.B. $$$CB$$$WCA38KSKU9XTODCO:ack gesendet für den Acknowledge von Host 10.44.4.5 / Service PING.

OMD[dev1]:~$ tg_admin --show-notify-history
ID  Callback ID         Host                Service                                 Datum               Chat_ID   Username  
----------------------------------------------------------------------------------------------------------------------------------
232 WCA38KSKU9XTODCO    10.44.4.5           PING                                    2017-08-21 13:01:50 123456789 cmkadmin  
233 T46ETYJ1CH7MV2ZS    10.44.4.5           Check_MK Discovery                      2017-08-21 13:01:54 123456789 cmkadmin  
234 ADZJPKLMO0SV63WU    10.44.4.5           None                                    2017-08-21 13:02:02 123456789 cmkadmin  
235 4WLYX43CGRJJQNHX    10.44.4.4           None                                    2017-08-21 13:12:13 123456789 cmkadmin  
236 6OMRDN6DQ1KAIWY4    10.44.4.5           None                                    2017-08-21 13:12:16 123456789 cmkadmin  
237 BUQ0HEAKPTFMLPED    10.44.4.5           Check_MK Discovery                      2017-08-21 13:31:11 123456789 cmkadmin  
238 3QP6YL48RNWGJV4V    10.44.4.5           PING                                    2017-08-21 13:40:00 123456789 cmkadmin  
239 7BKRA2ATVRHESFWH    10.44.4.5           None                                    2017-08-21 13:46:14 123456789 cmkadmin  
240 1MZ2X50KJ55ZXA2W    10.44.4.4           None                                    2017-08-21 13:46:16 123456789 cmkadmin  
241 N3RUH9EGYGJ314UU    10.44.4.6           PING                                    2017-08-21 13:47:16 123456789 cmkadmin  
242 0273U5QDMDH2RFQA    10.44.4.6           Check_MK Discovery                      2017-08-21 13:47:18 123456789 cmkadmin  
243 JZFJEUYOZAJ7U0VF    10.44.4.6           None                                    2017-08-21 13:47:29 123456789 cmkadmin  
244 0YSN5ZG4DW80NGT1    10.44.4.6           PING                                    2017-08-21 13:48:13 123456789 cmkadmin  
245 D436YY9GE3MLH00W    10.44.4.6           Check_MK Discovery                      2017-08-21 13:48:14 123456789 cmkadmin  

Die empfangenen Callbacks werden in der SQL Datenbank gespeichert und werden hier von dem Programm tg_callback verarbeitet und die entsprechenden Aufrufe gegen Check_MK durchgeführt. Mit „tg_admin –show-callbacks“ lassen sich die angeforderten Callbacks und der Status anzeigen.

OMD[dev1]:~$ tg_admin --show-callbacks
Callback ID         Host                Service                                 Datum               Command   Executed
----------------------------------------------------------------------------------------------------------------------------------
OWQML2LFDZM8LIB3    localhost           Interface 1                             2017-08-18 23:58:49 ack       1 
C6V7O11LAF65F5Q5    localhost           Check_MK Discovery                      2017-08-20 12:23:13 ack       1 
P81F439ZX54ET4SG    10.44.4.4           None                                    2017-08-20 12:53:15 ack       1 
CQHDP8OJFE4VTNHE    10.44.4.4           None                                    2017-08-20 13:04:06 ack       1 
ZZ3OAVP41OPWE1PG    localhost           OMD dev1 Notification Spooler           2017-08-20 13:08:46 ack       1 
2XKFHEVFGLB389QX    10.44.4.4           None                                    2017-08-20 13:34:00 down24h   1 
916IUVV6O66GBDUA    10.44.4.4           None                                    2017-08-20 13:49:45 ack       1 
916IUVV6O66GBDUA    10.44.4.4           None                                    2017-08-20 13:49:46 down24h   1 
ZPAZ4OJ63CJ7IEM7    localhost           OMD dev1 Notification Spooler           2017-08-21 11:57:28 ack       1 
ZPAZ4OJ63CJ7IEM7    localhost           OMD dev1 Notification Spooler           2017-08-21 11:57:29 down24h   1 
ADZJPKLMO0SV63WU    10.44.4.5           None                                    2017-08-21 13:12:02 ack       1 
T46ETYJ1CH7MV2ZS    10.44.4.5           Check_MK Discovery                      2017-08-21 13:31:02 ack       1 
WCA38KSKU9XTODCO    10.44.4.5           PING                                    2017-08-21 13:39:49 down24h   1 
ADZJPKLMO0SV63WU    10.44.4.5           None                                    2017-08-21 13:46:04 down24h   1 
N3RUH9EGYGJ314UU    10.44.4.6           PING                                    2017-08-21 13:48:02 ack       1 
0273U5QDMDH2RFQA    10.44.4.6           Check_MK Discovery                      2017-08-21 13:48:03 down24h   1 

Die ersten Notifications sind am Client angekommen:

Jeder einzelne Service lässt sich ACKen oder für 24h auf Downtime setzen. Klickt man die Buttons an dauert es max. 1 Minute und TG-Notify anwortet das er den Callback empfangen hat.

 

 

 

 

Check_MK: Problem mit Apache HTTP Proxy – SELinux blockt Reverse Proxy Verbindung zur Check_MK Instanz

Habe gerade auf ein frisch installiertes CentOS 7.4 Check_MK 1.4.0p19 installiert. Nach dem Start einer OMD Instanz kommt nur die Fehlermeldung:

OMD: Site Not Started

You need to start this site in order to access the web interface.

Im Apache Log ist folgendes zu sehen:

[Mon Dec 04 08:50:48.097245 2017] [proxy_http:error] [pid 20887] [client x.x.x.x:31372] AH01114: HTTP: failed to make connection to backend: 127.0.0.1, referer: http://server.example.net/extern/
[Mon Dec 04 08:50:56.943253 2017] [proxy:error] [pid 20883] (13)Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:5000 (127.0.0.1) failed
[Mon Dec 04 08:50:56.943276 2017] [proxy:error] [pid 20883] AH00959: ap_proxy_connect_backend disabling worker for (127.0.0.1) for 0s
[Mon Dec 04 08:50:56.943280 2017] [proxy_http:error] [pid 20883] [client x.x.x.x:31408] AH01114: HTTP: failed to make connection to backend: 127.0.0.1

netstat – tulpen zeigt aber das das Backend läuft:

# netstat -tulpen
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode      PID/Program name    
...
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      997        71127      21004/httpd         
...

Ein Blick in das Audit Log verrät das SELinux zuschägt:

#tail -f /var/log/audit/audit.log
...
type=AVC msg=audit(1512377448.096:3647): avc:  denied  { name_connect } for  pid=20887 comm="httpd" dest=5000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:commplex_main_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1512377448.096:3647): arch=c000003e syscall=42 success=no exit=-13 a0=a a1=559b02b25650 a2=10 a3=7fffb621631c items=0 ppid=20882 pid=20887 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1512377508.204:3689): avc:  denied  { name_connect } for  pid=21020 comm="httpd" dest=5000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:commplex_main_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1512377508.204:3689): arch=c000003e syscall=42 success=no exit=-13 a0=a a1=559b02b25650 a2=10 a3=7fffb621633c items=0 ppid=20882 pid=21020 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)
...

Das Problem kann temporär zum testen wie folgt gelöst werden:

/usr/sbin/setsebool httpd_can_network_connect 1

um die Änderung permanent zu übernehmen:

/usr/sbin/setsebool -P httpd_can_network_connect 1

 

Check_MK: Automation via Web Service

Wenn man Dinge automatisieren möchte kann man das über die Webservices von Check_MK machen. Dazu gibt es einen schönen Artikel von Check_MK selbst: https://mathias-kettner.de/checkmk_multisite_automation.html

Leider ist es aus der URL nicht leicht rauszufinden welche Variablen man mitgeben muss um z.B. einen Service zu Ack’en. Es gibt aber einen Trick um das einfach herauszufinden.

In den Global Settings unter User Interface -> Debug mode aktivieren.

Danach gewünschte Aktion ausführen und sich die Debug Ausgabe ansehen:

Hier sind jetzt alle Variablen aufgelistet die bei dem Request übergeben werden.

Viel Spaß beim automatisieren 😉

Check_MK: Agent Monitoring via SSH

Falls eine unverschlüsselte Abfrage des Check_MK Agemten nicht in Frage kommt ist es möglich den Agenten über SSH abzurufen.

SSH Key erzeugen in der OMD Umgebung

OMD[dev1]:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/omd/sites/dev1/.ssh/id_rsa): 
Created directory '/omd/sites/dev1/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /omd/sites/dev1/.ssh/id_rsa.
Your public key has been saved in /omd/sites/dev1/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:1Gxpgs9G9f4nK5uIvhe1iKU8xII1UzxFGZ7aApkYsNI dev1@cmkdev.m.local
The key's randomart image is:
+---[RSA 2048]----+
|  ...  o.o=o     |
| . . o++o=.+     |
|. E .o=++.O .    |
| .  . .*o*...    |
|       +S+.o..   |
|       .=.o ..   |
|         . .  o .|
|         ..... + |
|       .+o. oo.  |
+----[SHA256]-----+

Auf Zielsystem User anlegen, per sudo auf den Agenten berechtigen und SSH Publickey anlegen

User monitoring anlegen

root@target:~# adduser monitoring
Lege Benutzer »monitoring« an ...
Lege neue Gruppe »monitoring« (1003) an ...
Lege neuen Benutzer »monitoring« (1002) mit Gruppe »monitoring« an ...
Erstelle Home-Verzeichnis »/home/monitoring« ...
Kopiere Dateien aus »/etc/skel« ...
Geben Sie ein neues UNIX-Passwort ein: 
Geben Sie das neue UNIX-Passwort erneut ein: 
passwd: password updated successfully
Changing the user information for monitoring
Enter the new value, or press ENTER for the default
  Full Name []: Monitoring
  Room Number []: 
  Work Phone []: 
  Home Phone []: 
  Other []: 
Sind diese Informationen korrekt? [J/n] j

/etc/sudoers File anpassen

monitoring     ALL = NOPASSWD: /usr/bin/check_mk_agent

/home/monitoring/.ssh/authorized_keys anlegen

command="sudo /usr/bin/check_mk_agent" ssh-rsa AAAAB3NzaC..................GOXzCLX dev1@cmkdev.m.local

Rechte anpassen

chmod 640 /home/monitoring/.ssh/
chmod 600 /home/monitoring/.ssh/authorized_keys

xinetd Port 6556 abschalten

/etc/xinetd.d/check_mk anpassen

disable = yes

Anschließend „service xinetd restart“

Check_MK bekannt machen das Agent per SSH abgefragt werden muss

In WATO muss hierfür eine Regel angelegt werden.

Zu finden unter: Host & Service Parameters -> Datasource Programs -> Individual program call instead of agent access

Command line to execute:

ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no monitoring@$HOSTADDRESS$

Speichern und Regeln deployen, danach kann man mit WATO die Services suchen, etc.

Viel Spaß 😉

 

Python: Snippet – Python Code aus Textdateien ausführen/importieren

Check_MK speichert alle Daten in einfachen Dateien direkt als ausführbaren Python Code.

Um die abgelegten Dictionarys etc. in seinen eigenen Skripten weiterverwenden zu können kann man sich die mit eval() oder exec() laden.

eval() kann verwendet werden um z.B.  ein Dictionary in eine Variable zu laden, exec() kann auch ganze Funktionen etc. laden.

Beispiel eval():

dict.txt

{"foo":"bar","aaa":"bbb"}

import_dict.py

#!/usr/bin/env python

with open("dict1.txt","r") as f:
    x = eval(f.read().replace("\n",""))

print x

print x['foo']

Ergebnis:

max@cmkdevel:~/dev$ python import_dict.py
{'foo': 'bar', 'aaa': 'bbb'}
bar

Beispiel exec()

code.txt

max['foo'] = {
             "foo": "bar",
             "fxx": "boo",
             }


def hello(name):
    print "Hallo " + name

import_code.py

#!/usr/bin/env python

max = {}

with open("code.txt","r") as f:
    exec(f.read())


print max
print max['foo']
print max['foo']['foo']
hello("max")

Ergebnis:

max@cmkdevel:~/dev$ python import_code.py 
{'foo': {'foo': 'bar', 'fxx': 'boo'}}
{'foo': 'bar', 'fxx': 'boo'}
bar
Hallo max

 

 

Check_MK: Inventory erweitern mit SNMP Daten

Check_MK hat seit Version 1.2.5i1 das Feature Inventory an Board. Per Default werden von Systemen CPU, Memory, Harddisks, Softwarepakete und vieles mehr eingesammelt. Die Anleitung dient dazu das Inventory zu erweitern und eigene Informationen per SNMP einzusammeln und per View durchsuchbar zu machen.

Ich habe mittlerweile mehrere eigene Ergänzungen gemacht, darunter CDP Neighbors, Dot1X Authentications, Wireless Accesspoints und Hardware Inventory von Cisco Geräten.

Als Beispiel zeige ich das Cisco Hardware Inventory mit Seriennummern.

Bestandteile für eine Inventory Erweiterung:

  1. Daten sammeln -> Erweiterung für den Inventory Collector unter  ~/local/share/check_mk/inventory/
  2. Daten darstellen -> Erweiterung für die Inventory Views unter ~/local/share/check_mk/web/plugins/views/

1. Daten sammeln

Die Inventory Daten werden innerhalb einer Baumstruktur abgelegt. Man kann für seine eigenen Daten neue Knoten erzeugen. z.B. unter networking das hw_inventory wird im Tree dargestellt als networking.hw_inventory: . Der Doppelpunkt zeigt an das Daten unterhalb des Knotens folgen.

Das Inventory Plugin besteht aus einer Funktion und dem inv_info Directory welches alle Inventory Plugins kennt. In inv_info  im Key snmp_scan_function ist genauso wie bei den Check_MK SNMP Checks angegeben ob Check_MK überhaupt Daten finden wird bei einem SNMP Bulkwalk. snmp_info beinhaltet die OID und alle Untertabellen mit Daten die von Check_MK eingesammelt werden sollen.

Beispiel: ~/local/share/check_mk/inventory/my_hw_inventory.py

#!/usr/bin/python

port = {
0: "-",
1: "other",
2: "unknown",
3: "chassis",
4: "backplane",
5: "container",
6: "powerSupply",
7: "fan",
8: "sensor",
9: "module",
10: "port",
11: "stack",
12: "cpu"

}

def inv_hw_inventory(info, params):
    
    node = inv_tree("networking.hw_inventory:")

    for x in info[0]:
        
        #print x
        
        # If Serial Number is longer than 1 char
        if len(x[10]) >= 1:
        
            fru = "true" if "1" in x[15] else "false"
            
            classx = port[int(x[4])] if len(x[4]) is 1 else port[0]
            
            node.append(
            {
            "index": x[0],
            "description": x[1],
            "vendor_type": x[2],
            "contained_in_id": x[3],
            "class": classx,
            "parent_rel_pos": x[5],
            "name": x[6],
            "hw_rev": x[7],
            "fw_rev": x[8],
            "sw_rev": x[9],
            "serial_number": x[10],
            "manufactor": x[11],
            "model": x[12],
            "alias": x[13],
            "asset_id": x[14],
            "is_fru": fru,
            }
            )
        

inv_info["inv_hw_inventory"] = {
"inv_function": inv_hw_inventory,
"snmp_info": [(".1.3.6.1.2.1.47.1.1.1.1",[OID_END, # entPhysicalIndex
                                        "2", # entPhysicalDescr
                                        "3", # entPhysicalVendorType
                                        "4", # entPhysicalContainedIn
                                        "5", # entPhysicalClass
                                        "6", # entPhysicalParentRelPos
                                        "7", # entPhysicalName
                                        "8", # entPhysicalHardwareRev
                                        "9", # entPhysicalFirmwareRev
                                        "10", # entPhysicalSoftwareRev
                                        "11", # entPhysicalSerialNum                                        
                                        "12", # entPhysicalMfgName
                                        "13", # entPhysicalModelName
                                        "14", # entPhysicalAlias
                                        "15", # entPhysicalAssetID
                                        "16", # entPhysicalIsFRU
                                             ]),
             ],
"snmp_scan_function": lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.9.1"),
}

Mit der inv_tree Funktion wird ein neuer Inventory Baum eröffnet (siehe Code). Alle eingesammelten Informationen werden dann an diesen neuen Baum mit append als Dictionary hinzugefügt.

Die eigene Inventory Funktion kann man testen mit:

OMD[dev1]:~/local/share/check_mk/web/plugins/views$ cmk -v -i testswitch
Doing HW/SW-Inventory for testswitch...inv_hw_inventory snmp_extended_info snmp_os snmp_info ..unchanged..1603 entries..OK

Wenn in der Inventory Funktion ein pprint.pprint(info) eingebaut wird können auch die Rohdaten betrachtet werden.

2. Daten darstellen

Hierfür muss ein View gebaut werden der die Informationen des neuen Inventory Baums darstellt.

Beispiel ~/local/share/check_mk/web/plugins/views/my_inv_hw_inventory.py

#!/usr/bin/python

inventory_displayhints.update({

        ".networking.hw_inventory:"                          : { "title" : _("HW Inventory"), "render" : render_inv_dicttable,  "view" : "invhwi_of_host", "keyorder": 
                                                               ["index","name","description","model","contained_in_id","serial_number","hw_rev","fw_rev","sw_rev","class","manufactor","asset_id","alias","is_fru"] },
        ".networking.hw_inventory:*.index"                   : { "title" : _("Index") },
        ".networking.hw_inventory:*.description"             : { "title" : _("Description") },
        ".networking.hw_inventory:*.contained_in_id"         : { "title" : _("Contained in Index") },
        ".networking.hw_inventory:*.class"                   : { "title" : _("Class") },
        ".networking.hw_inventory:*.name"                    : { "title" : _("Name") },
        ".networking.hw_inventory:*.hw_rev"                  : { "title" : _("HW REV") },
        ".networking.hw_inventory:*.fw_rev"                  : { "title" : _("FW REV") },
        ".networking.hw_inventory:*.sw_rev"                  : { "title" : _("SW REV") },
        ".networking.hw_inventory:*.serial_number"           : { "title" : _("Serial Number") },
        ".networking.hw_inventory:*.manufactor"              : { "title" : _("Manufactor") },
        ".networking.hw_inventory:*.model"                   : { "title" : _("Model") },
        ".networking.hw_inventory:*.alias"                   : { "title" : _("Alias") },
        ".networking.hw_inventory:*.asset_id"                : { "title" : _("Asset ID") },
        ".networking.hw_inventory:*.is_fru"                  : { "title" : _("Is FRU") },
})

declare_invtable_view("invhwi", ".networking.hw_inventory:", _("HW Inventory"), _("HW Inventory"))

Danach sollte man mit „omd restart apache && cmk -v -R“ Check_MK und den Apachen neu starten, damit die neuen Views sichtbar werden. Damit Daten angezeigt werden muss 1 mal das Inventory auf dem Gerät gelaufen sein.

Im Views Menü sollte dann unser neuer View unter Inventory auftauchen:

Man kann sich alle Daten anzeigen lassen oder gezielt nach einzelnen Informationen oder Geräten suchen.

Wenn man im Host View ist kann man über das obere Menü auch direkt zu den Inventory Daten kommen, wurde der Button noch nicht genutzt kann er sichtbar gemacht werden mit dem „…“ Button.

Die Daten werden für den Host als Baumstruktur dargestellt.

Viel Spaß beim Erweitern 😉

Check_MK: Werte simulieren in einem SNMPwalk

Wer in Check_MK mittels gespeicherter SNMP Walks Checks entwickelt möchte unter umständen auch gerne sich ändernde Werte haben und keine statischen aus dem Dump.

Check_MK hat 3 eingebaute Funktionen um steigende Counter etc. zu simulieren.

Ausschnitt aus dem Check_MK Code lib/python/cmk_base/agent_simulator.py (GPLv2):

def agentsim_uptime(rate = 1.0, period = None): # period = sinus wave
    if period == None:
        return int(our_uptime() * rate)
    else:
        a = (rate * period) / (2.0 * math.pi)
        u = our_uptime()
        return int(u * rate + int(a * math.sin(u * 2.0 * math.pi / period)))


def agentsim_enum(values, period = 1): # period is in seconds
    hit = int(our_uptime()) / period % len(values)
    return values[hit]


def agentsim_sinus(base = 50, amplitude = 50, period = 300):
    return int(math.sin(our_uptime() * 2.0 * math.pi / period) * amplitude + base)



Wie kann man die Simulatoren verwenden?

Im gespeicherten SNMP Walk müssen die Simulatoren eingefügt werden statt des statisch exportieren Werts. Die SNMP Walks liegen unter ~/var/check_mk/snmpwalks/<hostname>

Beispiel:

.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.1.3 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.1 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.2 %{uptime(rate=10)}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.3 %{sinus()}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.1 %{enum([10,20,30,40,50,60,70,80,90])}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.2 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.3 31008
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.4.1 5353
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.4.2 0

 

 

Check_MK: Cisco MLS QoS Check

Cisco MLS QoS Check ist ein Check_MK kompatibler Check zum Überwachen der der Hardware Queues auf Cisco Switches.

Per Default werden keine Queues / Thresholds überwacht, diese müssen durch eine WATO Rule definiert werden.

Für jedes Interfaces wird ein Service angelegt, mehrere Queues auf einem Interface werden in einem Service dargestellt.

Die Queues werden einzeln als Graphen dargestellt:

 

 

Check_MK: Livestatus Schnittstelle

Check_MK stellt alle Informationen per Livestatus Schnittstelle bereit. So gut wie alles was man auf der Webseite des Monitorings sehen kann kommt aus Livestatus. Der Sprachsyntax für Abfragen ist LQL -> Livestatus Query Language.

Per Default ist die Livestatus Schnittstelle nur auf dem Host selbst über einen Socket abrufbar. Es gibt aber die Möglichkeit per xinetd die Schnittstelle auch per LAN zur Verfügung zu stellen.

Livestatus Schnittstelle auf LAN (per TCP) aktivieren

OMD stoppen

OMD[dev1]:~$ omd stop
Removing Crontab...OK
Stopping dedicated Apache for site dev1....OK
Stopping Check_MK Micro Core...killing 19502....OK
Stopping rrdcached...waiting for termination...OK
Stopping mknotifyd...killing 19481...
Stopping Livestatus Proxy-Daemon...killing 19474....OK
Stopping mkeventd...killing 19466....OK

OMD Config starten

OMD[dev1]:~$ omd config

OMD starten

OMD[dev1]:~$ omd start
Starting mkeventd...OK
Starting Livestatus Proxy-Daemon...OK
Starting mknotifyd...OK
Starting rrdcached...OK
Starting Check_MK Micro Core...OK
Starting dedicated Apache for site dev1...OK
Starting xinetd...OK
Initializing Crontab...OK
ACHTUNG
Nach dem Aktivieren der Livestatus Schnittstelle ist sie von jedem Host aus erreichbar! Wenn dies nicht gewünscht ist muss man das in der xinetd Konfig einschränken.

Erlaubte Hosts auf Livestatus einschränken

Dazu muss ~/etc/xinet.d/mk-livestatus angepasst werden beim Parameter only_from, anschließend muss der xinetd Service neu gestartet werden mit „omd restart xinetd“.

service livestatus
{
  type		= UNLISTED
  socket_type	= stream
  protocol	= tcp
  wait		= no

        # limit to 100 connections per second. Disable 3 secs if above.
  cps             = 100 3

        # set the number of maximum allowed parallel instances of unixcat.
        # Please make sure that this values is at least as high as 
        # the number of threads defined with num_client_threads in
        # etc/mk-livestatus/nagios.cfg
        instances       = 500

        # limit the maximum number of simultaneous connections from
        # one source IP address
        per_source      = 250 

        # Disable TCP delay, makes connection more responsive
  flags           = NODELAY
# configure the IP address(es) of your Nagios server here:
  only_from       = 127.0.0.1 10.0.20.1 10.0.20.2

# ----------------------------------------------------------
# These parameters are handled and affected by OMD
# Do not change anything beyond this point.

# Disabling is done via omd config set LIVESTATUS_TCP [on/off].
# Do not change this:
  disable		= no

# TCP port number. Can be configure via LIVESTATUS_TCP_PORT
  port		= 6557

# Paths and users. Manual changes here will break some omd
# commands such as 'cp', 'mv' and 'update'. Do not toutch!
  user		= dev1
  server		= /omd/sites/dev1/bin/unixcat
  server_args     = /omd/sites/dev1/tmp/run/live
# ----------------------------------------------------------
}

Livestatus Schnittstelle nutzen

Libraries und Beispiele von Check_MK

Check_MK hat in seinen DOCs für Python, Perl und C++ Libraries und Beispiele an Board. Zu finden sind Sie unter ~/share/doc/check_mk/livestatus/api/

Hier ein einfaches Beispiel für Python:

#!/usr/bin/env python

import os
import sys
import livestatus

try:
    omd_root = os.getenv("OMD_ROOT")
    socket_path = "unix:" + omd_root + "/tmp/run/live"

except:
    sys.stderr.write("This example is indented to run in an OMD site\n")
    sys.stderr.write("Please change socket_path in this example, if you are\n")
    sys.stderr.write("not using OMD.\n")
    sys.exit(1)

department = "testx"

hosts = livestatus.SingleSiteConnection(socket_path).query_table("GET hosts\nColumns: name\nFilter: custom_variables ~~ TAGS %s" % department)

for host in hosts:
    print host[0]

Test Run:

OMD[dev1]:~$ python live.py 
router

PHP Library

Check_MK hat selbst keine Library für PHP an Board, ich habe aber bereits folgende erfolgreich eingesetzt.

https://github.com/aashley/nagios-livestatus-client

Beispiel von der Github Seite (siehe Link oben)

<?php

use Nagios\Livestatus\Client;

$options = array(
    'socketType' => 'tcp',
    'socketAddress' => '10.253.14.22',
    'socketPort' => '6557',
);

$client = new Client($options);

$response = $client
    ->get('hosts')
    ->column('host_name')
    ->column('state')
    ->execute();

foreach ($response as $host) {
    print $host[0] . ": " . $host[1] . "\n";
}

$response = $client
    ->get('hosts')
    ->column('host_name')
    ->column('state')
    ->executeAssoc();

foreach ($response as $host) {
    print $host['host_name'] . ": " . $host['state'] . "\n";
}

Auf dem Check_MK Host

root@cmkdev:~# echo -e "GET hosts\nColumns: host_name\n" | unixcat /omd/sites/dev1/tmp/run/live
cluster
node1
router
node3
node2
localhost
Hinweis: Auf Ubuntu muss check-mk-livestatus installiert sein damit unixcat zur Verfügung steht.

In der OMD Umgebung

In der OMD Umgebung steht das Tool lq zur Verfügung welches ebenfalls ein unixcat macht.

OMD[dev1]:~$ lq "GET hosts\nColumns: host_name\n" 
cluster
node1
router
node3
node2
localhost

per TCP von Remote Host

Der Standardport ist tcp/6557.

root@cmkdev2:~# echo -e "GET hosts\nColumns: host_name\n" | netcat 10.211.55.13 6557
cluster
node1
router
node3
node2
localhost

Welche Tabellen / Spalten (Columns) gibt es?

Das lässt sich per Livestatus abfragen:

OMD[dev1]:~$ lq "GET columns\nFilter: table = hosts"

description;name;table;type
Whether passive host checks are accepted (0/1);accept_passive_checks;hosts;int
Whether the current problem has been acknowledged (0/1);acknowledged;hosts;int
Type of acknowledgement (0: none, 1: normal, 2: sticky);acknowledgement_type;hosts;int
An optional URL to custom actions or information about this host;action_url;hosts;string
The same as action_url, but with the most important macros expanded;action_url_expanded;hosts;string
Whether checks of the object are enabled (0/1);active_checks_enabled;hosts;int
IP address;address;hosts;string
An alias name for the host;alias;hosts;string
Logical command name for active checks;check_command;hosts;string
Logical command name for active checks, with macros expanded;check_command_expanded;hosts;string
Whether to check to send a recovery notification when flapping stops (0/1), not supported by CMC;check_flapping_recovery_notification;hosts;int
Whether freshness checks are enabled (0/1);check_freshness;hosts;int
Number of basic interval lengths between two scheduled checks;check_interval;hosts;float
The current check option, forced, normal, freshness (0-2), not supported by CMC;check_options;hosts;int
Time period in which this object will be checked. If empty then the check will always be executed.;check_period;hosts;string
Type of check (0: active, 1: passive);check_type;hosts;int
Whether checks of the object are enabled (0/1);checks_enabled;hosts;int
A list of all direct children of the host;childs;hosts;list
... gekürzt ...

Hilfe beim Zusammenstellen eines LQL Query

Da LQL Queries sehr komplex werden können kann man das Check_MK zur Hilfe nehmen. Dazu muss in den Global Settings „Debug Livestatus queries“ aktivieren.

Anschließend kann man sich einen View zusammenstellen mit den gewünschten Daten, der LQL Query wird unten an der Webseite angezeigt der für die Abfrage verwendet wurde.

Mehr dazu bei Check_MK / Mathias Kettner

Mathias Kettner hat Livestatus entwickelt und bietet auch eine sehr gute Dokumentation dazu an:

https://mathias-kettner.de/checkmk_livestatus.html

Have fun 😉

 

Check_MK: Cluster Checks bauen

Hier ein kleines Beispiel wie man einen Cluster Check implementieren kann.

Testumgebung:

3 Nodes node1,node2 und node3 welche als Clusterobjekt cluster zusammengefasst sind. Die Daten kommen von Piggybacks.

Clustercheck ist mycluster und ist ein Beispiel Check.

Schritt 1: Nodes anlegen

In WATO 3 Nodes anlegen mit der IP 127.0.0.1 (Testumgebung, Daten kommen von Piggybacks), Agent: No Agent 

Schritt 2: Clusterobjekt anlegen

Schritt 4: Clustercheck bauen, Piggybacks & Inventory

Unser eigener Check wird in ~/local/share/check_mk/checks/ abgelegt, er heisst mycluster.

#!/usr/bin/env python

def inventory_mycluster(info):
    for line in info:
        #pprint.pprint(line)
        if "CLUSTER" in line[0]:
            return [(None, None)]


def check_mycluster(item, params, info):
    active_nodes = 0
    for node in info:
        key, value = node
        if "CLUSTER" in key:
            if "ACTIVE" in value:
                active_nodes += 1

    if active_nodes <= 1:
        status = 2
    else:
        status = 0
    return status, "%d active nodes" % active_nodes


check_info['mycluster'] = {
    'inventory_function'    : inventory_mycluster,
    'check_function'        : check_mycluster,
    'service_description'   : 'MYCluster',
}

Piggybacks ablegen in /var/lib/check_mk_agent/spool/mycluster_test

<<<<node1>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node2>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node3>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node4>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>

Inventory durchführen:

OMD[dev1]:~/local/share/check_mk/checks$ cmk -v -II cluster
Discovering services on cluster:
node1:
Using piggyback information from host localhost.
    1 mycluster

node2:
Using piggyback information from host localhost.
    1 mycluster

node3:
Using piggyback information from host localhost.
    1 mycluster

Reload durchführen:

OMD[dev1]:~/local/share/check_mk/checks$ cmk -R
Generating configuration for core (type cmc)...OK
Packing config...OK
Restarting monitoring core...OK

Schritt 5: Service zum Cluster Service machen

Eine neue WATO Regel erstellen „Clustered services“:

WATO Änderung aktivieren, die Services verschwinden bei den Nodes und werden nur noch unter dem Cluster Objekt angezeigt.

 

 

Viel Spaß 🙂

DNS Blacklist Check

Check um zu Überprüfen ob ein Mailserver auf einer Blacklist ist. Der Check lässt sich auch in Check_MK einbinden, dazu das Script in ~/local/lib/nagios/plugins/ ablegen und eine Regel „Classical active and passive Monitoring checks“ für den Mailserver erstellen. Eine native Check_MK Implementierung folgt noch 😉

#!/usr/bin/env python
# -*- encoding: utf-8; py-indent-offset: 4 -*-

#
# check_dnspl.py - Check IP against Blacklist
# Use it on your own risk!
#
# Written 2017 - Maximilian Thoma
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program; if not,
# write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
#

import getopt
import sys
import socket

# Define blacklists to be checked
blacklists = [
    'all.s5h.net',
    'b.barracudacentral.org',
    'bl.emailbasura.org',
    'bl.spamcannibal.org',
    'bl.spamcop.net',
    'blacklist.woody.ch',
    'bogons.cymru.com',
    'cbl.abuseat.org',
    # 'cdl.anti-spam.org.cn',
    'combined.abuse.ch',
    'db.wpbl.info',
    'dnsbl-1.uceprotect.net',
    'dnsbl-2.uceprotect.net',
    'dnsbl-3.uceprotect.net',
    'dnsbl.anticaptcha.net',
    'dnsbl.cyberlogic.net',
    'dnsbl.dronebl.org',
    'dnsbl.inps.de',
    'dnsbl.sorbs.net',
    'drone.abuse.ch',
    'drone.abuse.ch',
    'duinv.aupads.org',
    'dul.dnsbl.sorbs.net',
    'dyna.spamrats.com',
    'dynip.rothen.com',
    'exitnodes.tor.dnsbl.sectoor.de',
    'http.dnsbl.sorbs.net',
    'ips.backscatterer.org',
    'ix.dnsbl.manitu.net',
    'korea.services.net',
    'misc.dnsbl.sorbs.net',
    'noptr.spamrats.com',
    'orvedb.aupads.org',
    'pbl.spamhaus.org',
    'proxy.bl.gweep.ca',
    'psbl.surriel.com',
    'relays.bl.gweep.ca',
    'relays.nether.net',
    'sbl.spamhaus.org',
    'short.rbl.jp',
    'singular.ttk.pte.hu',
    'smtp.dnsbl.sorbs.net',
    'socks.dnsbl.sorbs.net',
    'spam.abuse.ch',
    'spam.dnsbl.sorbs.net',
    'spam.spamrats.com',
    'spambot.bls.digibase.ca',
    'spamrbl.imp.ch',
    'spamsources.fabel.dk',
    'ubl.lashback.com',
    'ubl.unsubscore.com',
    'virus.rbl.jp',
    'web.dnsbl.sorbs.net',
    'wormrbl.imp.ch',
    'xbl.spamhaus.org',
    'z.mailspike.net',
    'zen.spamhaus.org',
    'zombie.dnsbl.sorbs.net',
]


def check_if_valid_host_ip(ip):
    try:
        socket.inet_aton(ip)
        return True
    except socket.error:
        return False


def revert_ip(ip):
    x = ip.split('.')
    return x[3] + '.' + x[2] + '.' + x[1] + '.' + x[0]


def bls(olist):
    x = ''
    for bl in olist:
        x += bl + " "
    return x


def log(debug, s):
    if debug:
        print s


def usage():
    print "check_dnsbl.py - Check IP against DNS blacklists.\n" \
          " -H, --host <hostname or ip> Hostname or IP\n" \
          " -d, --debug Debug Modus\n" \
          " -h, --help Help"


def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "H:dh", ['host=', 'debug', 'help'])
    except getopt.GetoptError as err:
        print str(err)
        sys.exit(2)

    found_h = False
    host = None
    debug = False

    for o, a in opts:
        if o in ('-H', '--host'):
            host = a
            found_h = True
        if o in ('-d', '--debug'):
            debug = True
        if o in ('-h', '--help'):
            usage()
            sys.exit(2)

    if not found_h:
        print "-H is not given"
        usage()
        sys.exit(2)

    # print host
    # print debug



    # Check if valid Host IP
    if check_if_valid_host_ip(host) is not True:
        try:
            resolved_ip = socket.gethostbyname(host)
        except socket.gaierror:
            sys.stderr.write('Unable to make an DNS lookup, provided IP or hostname is invalid.')
            sys.exit(2)
        if check_if_valid_host_ip(resolved_ip) is not True:
            sys.stderr.write('Error no valid IP address.')
            sys.exit(2)
        else:
            ip = resolved_ip
    else:
        ip = host

    # Revert IP
    rip = revert_ip(ip)

    # Init variables
    negative_result_buffer = []

    for bl in blacklists:
        # Init Result
        result = ''
        # Build query string
        q = rip + '.' + bl
        log(debug, q)

        # Query DNS
        try:
            result = socket.gethostbyname(q)
            log(debug, "Result: %s" % result)
        except socket.error:
            log(debug, "No result")
            pass

        if "127.0.0" in result:
            log(debug, "Found 127.0.0 in result.")
            negative_result_buffer.append(bl)

    if len(negative_result_buffer) == 0:
        print "OK - %s (%s) is not listed at: %s" % (host, ip, bls(blacklists))
        sys.exit(0)
    else:
        print "CRITICAL - %s (%s) ist listed at: %s" % (host, ip, bls(negative_result_buffer))
        sys.exit(2)


if __name__ == "__main__":
    main()

 

Check_MK: Host custom variables

Check_MK bietet auch die Möglichkeit die Host Variablen zu erweitern um z.B. ein Host Description Feld hinzuzufügen.

Die Erweiterung muss in ~/local/share/check_mk/web/plugins/wato/ abgelegt werden. Die Dateiendung ist zwingend .py.

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ cat host_custom_vars.py 

#!/usr/bin/env python

declare_host_attribute(
    NagiosTextAttribute(
        # Sektion / Gruppe
        "MY_HOST_CUSTOM_VARIABLES",
        # Variable mit beginnenden Unterstrich
        "_HOST_MAGIC",
        # Feldbeschreibung
        _("Host Magic"),
        # Hilfe
        _("Hilfe"),
    ),
    show_in_table=True,
    show_in_folder=True,
)

Nach dem Anlegen eines eigenen Attributs muss der Apache neu gestartet werden mit z.B. „omd restart apache“.

Die Host custom variables sind auch per Livestatus abrufbar:

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ lq "GET hosts\nColumns: host_name host_address\nFilter: host_custom_variables ~ HOST_MAGIC (^|[ ])magic($|[ ])"
localhost;127.0.0.1

 

Check_MK: Service custom variables

Die Service Variablen lassen sich genau so Erweitern wie die Host Variablen.

Dazu muss man eine neue Definition erstellen im Folder ~/local/share/check_mk/web/plugins/wato/. Die Dateiendung muss .py sein.

#!/usr/bin/env python

register_rule(
    # Sektion
    "Test",
    # Variable
    "extra_service_conf:_test_test",
    # Parameter
    TextUnicode(
        title = _("TEST TEST"),
        help = _("Test test"),
        size = 80,
        attrencode = True,
    ),
    itemtype = 'service',
    match = 'first',
)

Die Variable in der das gespeichert wird ist: extra_service_conf:_test_test, in Livestatus ist es dann unter service_custom_variables als TEST_TEST abrufbar.

Danach ein „omd restart apache && cmk -R“ um die Änderung zu aktivieren.

Jetzt kann mann unter „Parameters for this service“ einen Wert für den Service setzen.

Die selbst festgelegten Parameter können auch per Livestatus abgefragt werden:

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ lq "GET services\nColumns: service_host_name service_service_description\nFilter: service_custom_variables ~ TEST_TEST (^|[ ])test123($|[ ])"
localhost;CPU utilization

 

Check_MK: Script um IP Adresse statisch bei einem Host einzutragen

Wenn die Hosts nur mit DNS-Namen angelegt sind wird regelmäßig der DNS-Cache aktualisiert, dies verzögert den Deploy Prozess. Das Script ließt den DNS Cache aus und setzt für die Hosts die IP Adresse statisch, ist die IP nicht im Cache löst er diese direkt per DNS auf.

#!/usr/bin/env python

#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but WITHOUT ANY 
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with 
# this program; if not, write to the Free Software Foundation, Inc., 
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
#

import json
import os
import subprocess
import sys
import livestatus
import socket

url = "http://cmkdev/dev1/check_mk/webapi.py"
user = "checkmk_automation"
passw = "password"
department = "hosttag"

##########################################################################################
### DON'T CHANGE ANYTHING BELOW
##########################################################################################

curl_base_cmd = url + "?_username=%s&_secret=%s" %(user,passw)

def execute_curl_cmd(curl_cmd):
    try:
        p = subprocess.Popen(curl_cmd, shell=True, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        o, e = p.communicate()
        out  = json.loads(o)
    except Exception, e:
        sys.stderr.write("ERROR: '%s' while executing '%s'\n" % (e, curl_cmd))
        sys.exit(1)
        return {}

    if out["result_code"] == 0:
        return out["result"]
    else:
        sys.stderr.write("ERROR: while executing WEB API command: '%s'\ncurl: '%s'\n" % \
                         (out["result"], curl_cmd))
        sys.exit(1)
        return {}

def edit_wato_host(curl_base_cmd, req):
    create_cmd = ('curl --insecure \'' + curl_base_cmd + \
                  '&action=edit_host\' ' + \
                  '-d \'%s\'') % req
    return execute_curl_cmd(create_cmd)

try:
    omd_root = os.getenv("OMD_ROOT")
    socket_path = "unix:" + omd_root + "/tmp/run/live"
    ip_cache = omd_root + "/var/check_mk/ipaddresses.cache"

    with open(ip_cache,'r') as inf:
        ipc = eval(inf.read())

except:
    sys.stderr.write("This example is indented to run in an OMD site\n")
    sys.stderr.write("Please change socket_path in this example, if you are\n")
    sys.stderr.write("not using OMD.\n")
    sys.exit(1)

hosts_arr = livestatus.SingleSiteConnection(socket_path).query_table("GET hosts\nColumns: name\nFilter: custom_variables ~~ TAGS %s" % department)

for host in hosts_arr:

    cached_ip = ipc.get((host[0], 4))

    if cached_ip != None:

        print host[0]
        print cached_ip

        req = 'request={"hostname": "%s", "attributes":{"ipaddress": "%s"}}' %(host[0],cached_ip)

        print req

        edit_wato_host(curl_base_cmd,req)

    else:

        try:
            ip = socket.gethostbyname(host[0])
            print "%s - %s" % (host[0],ip)

        except:
            print "no dns record for host %s " % host[0]

        req = 'request={"hostname": "%s", "attributes":{"ipaddress": "%s"}}' %(host[0],ip)

        print req

        edit_wato_host(curl_base_cmd,req)

 

Check_MK: yield statt return in der Check Funktion

Check_MK erwartet bei der Check Funktion ein Tuple mit 2 (ohne Performance Daten) bzw. 3 Werten (mit Performance Daten).

<STATUS>, <MESSAGE>, <PERFOMANCEDATA>

Wenn man in einem Service mehrere Werte aggregieren möchte z.B. man hat mehrere Lüfter im System möchte aber nicht für jeden einen eigenen Service sondern nur einen einzigen, so muss man die Werte von mehreren vergleichen und dann den entsprechenden Status zurückgeben. Da diese ganzen Vergleiche aufwändig sind und fehleranfällig löst Check_MK so eine Situation mit dem yield Generator. Diesem werden alle Werte einfach übergeben, er setzt nach einer Logik den Servicestatus und fügt alle einzelnen Status in den Output des Services.

Der Status des Checks wird in folgender Reihenfolge angezeigt, d.H. ist einer der Status UNKNOWN wird der komplette Service als UNKNOWN angezeigt:

  1. Unknown
  2. Critical
  3. Warn
  4. OK

Beispiel alte Lösung mit 1 return

def check_mycheck(item, params, info):
    warn = False
    crit = False
    unkn = False

    msg = ""

    for i in info:
        fan, status = i
        status = int(status) 

        if status == 2:
            crit = True
            msg += " %s: CRIT -" % fan
        elif status == 1:
            warn = True
            msg += " %s: WARN -" % fan
        elif status == 3:
            unkn = True
            msg += " %s: UNKN -" % fan
        else:
            msg += " %s: OK -" % fan

    if unkn == True:
        return 3, msg
    elif crit == True:
        return 2, msg
    elif warn == True:
        return 1, msg
    else:
        return 0, msg

Ausgabe:

Beispiel neue Lösung mit yield

def check_mycheck(item, params, info):
    for i in info:
        fan, status = i
        status = int(status)
        yield status, "Status von: %s" % fan

Ausgabe:

Check_MK: Piggyback Checkergebnisse anderer Hosts bereitstellen

Es gibt die Möglichkeit Check Ergebnisse wenn ein Host nicht direkt erreichbar ist für Check_MK oder man bestimmte Dinge z.B. Erreichbarkeit eines TCP Services von einem anderen Host aus testen möchte, als Piggyback über den Agenten eines anderen Hosts mitzugeben.

In den Beispiel haben wir einen Host (router) der ein Check Ergebnis von einem anderen Host (localhost) bekommen soll. Check_MK verwendet hierfür folgenden Syntax:

<<<<hostname>>>>

Check Ergebnisse, Ausgaben von Local Checks, etc.

<<<<>>>>

Das Piggyback File muss auf dem Host mit Agenten unter /var/lib/check_mk_agent/spool abgelegt werden. Ich habe die Ausgabe für ein Agent Check + einen Local Check eingebaut.

root@cmkdev:/var/lib/check_mk_agent/spool# cat piggy_router 

<<<<router>>>>
<<<mycheck>>>
foo 1
bar 2

<<<local>>>
0 Mein_piggyback_localcheck - TEST TEST TEST

<<<<>>>>

Dazu ein einfacher Check der in der Zeile den 2 Parameter als Status für den ersten zurückgibt:

OMD[dev1]:~/local/share/check_mk/checks$ cat mycheck 
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-


def inventory_mycheck(info):
    if len(info) > 1:
        yield '', None


def check_mycheck(item, params, info):
    for i in info:
        yield int(i[1]), "Status von: %s" % i[0]



check_info['mycheck'] = {
    'inventory_function'    : inventory_mycheck,
    'check_function'        : check_mycheck,
    'service_description'   : 'MyCheck',
}

Inventory, Reload und Test:

OMD[dev1]:~$ cmk -v -II router
Discovering services on router:
router:
Using piggyback information from host localhost.
    1 local
    1 mycheck

OMD[dev1]:~$ cmk -R
Generating configuration for core (type cmc)...OK
Packing config...OK
Restarting monitoring core...OK

OMD[dev1]:~$ cmk -v router
Check_MK version 1.4.0p8
Using piggyback information from host localhost.
Mein_piggyback_localcheck OK - TEST TEST TEST
MyCheck              CRIT - Status von: foo(!), Status von: bar(!!)
OK - execution time 0.0 sec|execution_time=0.002 user_time=0.000 system_time=0.010 children_user_time=0.000 children_system_time=0.000

Check_MK weist auf der Shell darauf hin das Piggyback Informationen verwendet wurden:

Using piggyback information from host localhost.

 

Viel Spaß beim piggybacken 😉

Check_MK: Mit gespeicherten SNMP Walks Checks entwickeln

Es ist keine schlechte Idee seine Checks auf einer Testinstanz zu entwickeln, geht etwas schief wird das Produktionssystem davon nicht beeinflusst.

Check_MK bietet die Möglichkeit einen SNMP Walk zu exportieren und diesen für die Checkentwicklung zu verwenden.

Schritt 1: Export eines SNMP Walks in Check_MK

OMD[test1]:~$ cmk -v --snmpwalk router
router:
Walk on ".1.3.6.1.2.1"...2939 variables.
Walk on ".1.3.6.1.4.1"...1539 variables.
Successfully Wrote /omd/sites/test1/var/check_mk/snmpwalks/router.

Mit dem Befehl „cmk –snmpwalk <hostname>“ lässt sich auf dem Produktivsystem ein Check_MK kompatibler SNMP Walk erzeugen der in der Site unter ~/var/check_mk/snmpwalks/ abgelegt wird.

Schritt 2: Check_MK Testinstanz erzeugen

root@max:~# omd create dev
Adding /opt/omd/sites/dev/tmp to /etc/fstab.
Creating temporary filesystem /omd/sites/dev/tmp...OK
Restarting Apache...OK
Created new site dev with version 2017.07.11.cee.

  The site can be started with omd start dev.
  The default web UI is available at http://max/dev/

  The admin user for the web applications is cmkadmin with password: xxxxxxxxxxx
  (It can be changed with 'htpasswd -m ~/etc/htpasswd cmkadmin' as site user.
)
  Please do a su - dev for administration of this site.

root@max:~# omd start dev
Starting mkeventd...OK
Starting liveproxyd...OK
Starting mknotifyd...OK
Starting rrdcached...OK
Starting cmc...OK
Starting apache...OK
Initializing Crontab...OK
root@max:~#
  1. omd create dev
  2. omd start dev

Schritt 3: Kopieren des Walks

root@max:~# cp /omd/sites/prod/var/check_mk/snmpwalks/router /omd/sites/dev/var/check_mk/snmpwalks/router

Schritt 4: Host anlegen & SNMP walk Regel erstellen

Der Host muss genau so heissen wie das Walk-File, als IP-Adresse 127.0.0.1 angeben und bei Agent Type SNMP.

Anschließend noch eine Regel erstellen das die Walks verwendet werden für den Host.

Danach die Änderungen deployen.

Schritt 5: Inventory durchführen & Aktivieren

  1. Inventory: cmk -v -II router
  2. Aktivieren: cmk -v -O
  3. cmk -v -D router
OMD[dev]:~$ cmk -v -II router
Discovering services on router:
router:
    1 hr_cpu
    4 hr_fs
    1 hr_mem
    2 if64
    1 snmp_info
    1 snmp_uptime
    1 ucd_cpu_load

OMD[dev]:~$ cmk -v -O
Waiting for exclusive lock on /omd/sites/dev/etc/check_mk/main.mk.
Generating configuration for core (type cmc)...
Not importing state from Nagios, /omd/sites/dev/var/nagios/retention.dat not found.
/omd/sites/dev/var/check_mk/core/config written.
OK
Baking agents...VANILLA...linux_rpm:uptodate...linux_deb:uptodate...linux_tgz:uptodate...aix_tgz:uptodate...solaris_tgz:uptodate...solaris_pkg:uptodate...windows_msi:uptodate...OK
GENERIC...linux_rpm:uptodate...linux_deb:uptodate...linux_tgz:uptodate...aix_tgz:uptodate...solaris_tgz:uptodate...solaris_pkg:uptodate...windows_msi:uptodate...OK
router...does not use agent, skipping.
OK
Packing config...OK
Reloading monitoring core...OK
OMD[dev]:~$

OMD[dev]:~$ cmk -D router

router                                                                         
Addresses:              127.0.0.1
Tags:                   /wato/, ip-v4, ip-v4-only, lan, prod, site:dev, snmp, snmp-only, wato
Host groups:            
Contact groups:         all
Type of agent:          SNMP (use stored walk)
Services:
  checktype    item     params                                                                                                                                                                                                                        description         groups
  ------------ -------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------- ------
  ucd_cpu_load None     (5.0, 10.0)                                                                                                                                                                                                                   CPU load                  
  hr_cpu       None     {'levels': (80.0, 90.0)}                                                                                                                                                                                                      CPU utilization           
  hr_fs        /        {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /              
  hr_fs        /boot    {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /boot          
  hr_fs        /distros {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /distros       
  hr_fs        /opt/omd {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /opt/omd       
  if64         2        {'state': [u'1'], 'errors': (0.01, 0.1), 'speed': 1000000000}                                                                                                                                                                 Interface 2               
  if64         3        {'state': [u'1'], 'errors': (0.01, 0.1), 'speed': 100000000}                                                                                                                                                                  Interface 3               
  hr_mem       None     (150.0, 200.0)                                                                                                                                                                                                                Memory used               
  snmp_info    None     None                                                                                                                                                                                                                          SNMP Info                 
  snmp_uptime  None     {}                                                                                                                                                                                                                            Uptime                    
OMD[dev]:~$ 

Viel Spaß beim Entwickeln 😉