Archiv der Kategorie: Monitoring / Check_MK

Check_MK: SNMP based Check

Neben der Möglichkeit lokale Scripts per Agent oder Agent Plugins zu verwenden gibt es noch die Option Werte direkt per SNMP von Geräten auszulesen. Bei diesem Beispiel ist auch eine Erweiterung dabei damit in WATO eine Regel erstellt werden kann mit den Parametern für den Check. Performance Werte sind in dem Beispiel ebenfalls integriert.

Die anderen HowTos sind unter Agent Plugin Check und Local Check zu finden.

 

Aufbau eines SNMP Checks

Der SNMP Check besteht genau so wie der Agent based Check aus drei Bestandteilen.

  1. Inventory Funktion
  2. Check Funktion
  3. check_info Directory

Hier ein Beispiel Check dazu, der Check wird abgelegt in ~/local/share/check_mk/checks/:

#!/usr/bin/python

default_mycanon = (500,20000)

def inventory_mycanon(info):

    for i in info:
        idx, frag, pages = i
        yield idx, default_mycanon


def check_mycanon(item, params, info):

    perf=[]

    for i in info:
        idx, frag, pages_s = i

        if idx == item:
            warn, crit = params
            pages = int(pages_s)

    perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]


    if pages >= crit:
        status = 2
    elif pages >= warn:
        status = 1
    else:
        status = 0

    return status, "%d pages (w:%d/c:%d) %s" % (pages, warn, crit, get_item_state("buh")), perf


check_info["mycanon"] = {
    "inventory_function":   inventory_mycanon,
    "check_function":       check_mycanon,
    "service_description":  "Printer FooBar",
    "snmp_scan_function":   lambda oid: "canon" in oid(".1.3.6.1.2.1.1.1.0").lower(),
    "snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", "3", "4"]),
    "has_perfdata":         True,
    "group":                "mycanon",
}

Im Gegensatz zu den anderen Checks beginne ich in umgekehrter Reihenfolge mit den einzelnen Bausteinen weil in dem check_info Dictionary definiert ist wo die Inventory Funktion und die Check Funktion ihre Daten her bekommen.

Das check_info Dictionary

check_info["mycanon"] = {
    "inventory_function":   inventory_mycanon,
    "check_function":       check_mycanon,
    "service_description":  "Printer FooBar",
    "snmp_scan_function":   lambda oid: "canon" in oid(".1.3.6.1.2.1.1.1.0").lower(),
    "snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", "3", "4"]),
    "has_perfdata":         True,
    "group":                "mycanon",
}

Genauso wie bei dem Agent Check muss auch beim SNMP Check die Inventory Funktion und die Check Funktion angegeben werden. Die Service Description ist der Name unter dem später der Check in Check_MK im Host auftaucht.

Die snmp_scan_function wird verwendet um festzustellen ob Check_MK überhaupt auf diesem Gerät tätig wird. Nur wenn der Scan erfolgreich ist wird per SNMP BULK WALK die OIDs die in snmp_info angegeben sind abgerufen.

Bei dem Testgerät handelt es sich um einen Canon Drucker, bei einem SNMP Get auf die OID .1.3.6.1.2.1.1.1.0 gibt er folgenden String zurück „Canon iR C1021 /P“. Die Lambda Funktion überprüft ob in der OID .1.3.6.1.2.1.1.1.0 der String „canon“ vorkommt. Dabei wird die Rückgabe von oid(„.1.3.6.1.2.1.1.1.0“) mit lower() in Lowercase umgewandelt um den Vergleich zu vereinfachen.Hier gibt es noch verschiedene andere Möglichkeiten um zu bestimmen ob der Check zum Gerät passt. Am besten sich dazu die Check_MK eigenen Checks ansehen unter: ~/share/check_mk/checks

In snmp_info werden die OIDs angegeben die Abgerufen werden. Wenn es nur eine OID ist so ist ein Tuple mit OID und den entsprechenden einzelnen Untertabellen anzugeben. Sind es mehrere OIDs sind die Tuples in eine Liste zu verpacken.

"snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", 
                                                           "3",
                                                           "4"
                        ]),

In dem Beispiel rufen wir jetzt drei Untertabellen der OID .1.3.6.1.4.1.1602.1.11.1.4.1 ab. Diese werden an den Check für die Inventory Funktion und die Check Funktion als verschachtelte Liste übergeben.

Um das zu Testen geben wir die info in der Inventory Funktion mit pprint aus:

def inventory_mycanon(info):
    pprint.pprint(info)

Ausgabe auf der Shell:

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II printer-canon-c1021
Discovering services on printer-canon-c1021:
printer-canon-c1021:
[[u'101', u'', u'1'],
 [u'102', u'', u'1'],
 [u'104', u'', u'1'],
 [u'108', u'', u'8930'],
 [u'113', u'', u'8930'],
 [u'114', u'', u'2994'],
 [u'123', u'', u'11147'],
 [u'127', u'', u'18618'],
 [u'133', u'', u'7877'],
 [u'148', u'', u'10741'],
 [u'201', u'', u'1459'],
 [u'202', u'', u'1459'],
 [u'222', u'', u'1053'],
 [u'230', u'', u'406'],
 [u'231', u'', u'406'],
 [u'232', u'', u'406']]

In der Liste sind weitere Listen die den Werten aus der SNMP Anfrage entsprechen. Aus der ersten Liste [u’101′, u“, u’1′] ist der 1. Wert aus der 2. Untertabelle, der 2. Wert aus der 3. Untertabelle und der 3. Wert aus der 4. Untertabelle. (siehe oben bei snmp_info)

Inventory Funktion

default_mycanon = (500,20000)

def inventory_mycanon(info):

    for i in info:
        idx, frag, pages = i
        yield idx, default_mycanon

Die Inventory Funktion erkennt neue Services und legt diese in Check_MK an. Der Funktion wird das SNMP BULK WALK Ergebnis übergeben als Liste. (siehe oben) Diese einfache Inventory Funktion legt für jede Subliste einen neuen Service an. Dem yield Generator wird ein Tuple übergeben mit eindeutigem Servicenamen und den Default Parametern für den Check.

Check Funktion

def check_mycanon(item, params, info):

    perf=[]

    for i in info:
        idx, frag, pages_s = i

        if idx == item:
            warn, crit = params
            pages = int(pages_s)

    perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]


    if pages >= crit:
        status = 2
    elif pages >= warn:
        status = 1
    else:
        status = 0

    return status, "%d pages (w:%d/c:%d)" % (pages, warn, crit), perf

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.

Schritt 1: Aus info Liste das richtige Item suchen

for i in info:
       idx, frag, pages_s = i

       if idx == item:
           warn, crit = params
           pages = int(pages_s)

Die Liste wird durchgegangen und mit einer IF Anweisung wird verglichen ob es sich um den richtigen Datensatz handelt, wenn True wird das Parameter Tuple in die Variablen warn und vrit gespeichert. Der dritte Wert der Liste ist die Seitenanzahl und die muss noch von einem Unicode String umgewandelt werden zu einem Integer int(pages_s).

Schritt 2: Performance Werte

perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]

Die Performance Werte werden als Liste übergeben. In der Liste sind wiederum Tupels.

Der Tuple ist wie folgt aufgebaut:

(„<name_des_counters>“, <aktueller_wert>, <warn_wert>, <crit_wert>)

Nettes Feature in Check_MK:

Es gibt die Funktion get_rate welche Werte speichern kann und eine Rate pro Sekunde errechnen kann. Dazu ist eine eindeutige Identifier zu bestimmen und es muss die aktuelle Zeit mitgegeben werden damit Check_MK die Rate berechnen kann. Das gleiche gibt es auch für Average Werte, auch ist es möglich Werte ohne Berechung einfach zu speichern bis zur nächsten Check Ausführung (set_item_state(), get_item_state()).

Schritt 3: Status Logik

Der Status des Checks wird als 0 = OK, 1 = WARNING, 2 = CRITICAL oder 3 = UNKNOWN übergeben.

if pages >= crit:
    status = 2
elif pages >= warn:
    status = 1
else:
    status = 0

Schritt 4: Return oder Yield des Checkergebnisses

return status, "%d pages (w:%d/c:%d)" % (pages, warn, crit), perf

Als letztes wird ein Tuple zusammengebaut das aus 3 Werten besteht:

STATUS, MESSAGE, PERFORMANCEVALUES

Es gibt die Möglichkeit auch das dem yield Generator zu übergeben, statt return wird dann yield verwendet. Check_MK aggregiert dann alle Ergebnisse nacheinander. Dazu kommt in einem anderen Artikel noch weitere Informationen.

WATO Erweiterung für eigene Parameter

Ich habe in unserem Beispiel default Parameter definiert die für jeden Check angelegt werden. Wenn man jetzt für einen Host, Service oder Host/Service die Werte anpassen möchte und es bequem über WATO erledigen will muss man WATO erweitern.

WATO Erweiterungen befinden sich unter ~/lokal/share/check_mk/web/plugins/wato/ und müssen die Dateiendung .py haben. Auch muss zwingend der Apache neu gestartet werden. Das geht auf der OMD Konsole mit „omd restart apache“.

#!/usr/bin/python

register_check_parameters(
    # Category
    "Printers",
    # Group
    "mycanon",
    # Rulename
    "Printer pages max values",
    #Parameter
    Tuple(
        title = "Printer pages max value",
        help = "Define critical and warning values",
        elements = [
            Integer(title="Warn at", unit="pages"),
            Integer(title="Critical at", unit="pages"),
        ]
    ),
    #Item
    TextAscii(
        title = "Item Name",
        help = "Help for the item"
    ),
    #Match
    match_type = "first"
)

Wir haben als default Parameter ein Tuple gespeichert mit 2 Werten, Warn und Crit. Dementsprechend muss unsere WATO Regel ebenfalls ein Tuple mit 2 Werten erzeugen.

Die Funktion register_check_parameters() erzeugt eine neue möglichkeit Regeln zu definieren.

Die Funktion erwartet 6 Parameter:

1 Kategorie – Die Kategorie ist die Rubrik unter der die neue Regel angezeigt wird.

2 Group – Die Gruppe macht die Zuordnung zum Check. Im Check im check_info gibt es hierfür im Dictionary den Key „group“. (siehe oben im Beispiel)

3 Rulename – Der Rulename wird in der Übersicht angezeigt

4 Parameter – Hier werden die Parameter definiert.

5 Item – Es gibt hier 2 Möglichkeiten:

None – Die Regel kann nur für den Host definiert werden und gilt für alle Services die auf die Group matchen.

TextAscii( title = „Item Name“, help = „Help for the item“ ) –  Hier kann ein Servicename definiert werden, die Regel gilt dann nur für einen bestimmten Servicenamen

6 Matchtype – Es gibt 2 Matchtypen:

first – Die erste Regel die Zutrifft wird angewendet.

dict – Eine Kombination aus mehreren Werten ist möglich aus mehreren Regeln. Der Typ ist zwingend wenn der äußere Parametertyp Dictionary ist.

Der Parameter Block

#Parameter
    Tuple(
        title = "Printer pages max value",
        help = "Define critical and warning values",
        elements = [
            Integer(title="Warn at", unit="pages"),
            Integer(title="Critical at", unit="pages"),
        ]
    ),

Das äußere Tuple() ist eine von Check_MK bereitgestellte Funktion, es gibt noch viele weitere (z.B. TextAscii, Integer, Float, ListOf, Dictionary, etc.) Weitere Beispiele findet ihr direkt im Check_MK Code unter ~/share/check_mk/web/plugins/wato/.

Das Tuple() hat in diesem Fall 3 Übergabewerte:

title – Gibt den Titel der Werte an

help – Wenn die Hilfe aktiviert ist (Buch rechts oben in der Ecke) werden die Hilfstexte angezeigt

elements – Sind die Werte die in WATO eingegeben werden können, diese können beliebig oft, entsprechend dem Datenformat, verschachtelt werden. In elements befinden sich unsere 2 Integer() Werte Warn und Crit.

Zu den WATO Erweiterungen wird noch ein gesonderter Artikel folgen den ich hier verlinken werde.

Fertig ??

Jetzt können wir den Check mal Testen, dazu in WATO ein „Tabula Rasa“ für den Host machen, der neue Check sollte dann auftauchen.

Viel Spaß beim Spielen 🙂

 

Check_MK: Helferlein und Treasures

Check_MK hat ein paar kleine Helferlein & Treasures an Board die nicht jeder kennt.

mkcheck

Mkcheck ist ein Script das ein Template für einen neuen eigenen Check erzeugt.

Zu finden ist es unter ~/share/doc/check_mk/helpers/mkcheck

OMD[test1]:~$ ~/share/doc/check_mk/helpers/mkcheck -h

DESCRIPTION:
  This script creates a basic check file or if '-m'
  specified a manual page.
  You have to specify at least one name.

  The created check or manual page are saved to
  the current directory.

  It is recommended to run this script in the checks folder
  if you want to create a check or in the manual page folder
  if you want to create a man page.

USAGE: mkcheck [OPTS] ARGS

OPTIONS:
 -h, --help        Show this help
 -s                Adds SNMP info and scan function
 -p                Adds parse function
 -m                Creates the manual page instead of
                   the check

Beispiel für einen SNMP Check:

OMD[test1]:~$ ~/share/doc/check_mk/helpers/mkcheck -sp my_snmp_check
OMD[test1]:~$ cat my_snmp_check 

#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2017             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.


# example output


def parse_my_snmp_check(info):
    pprint.pprint(info)
    parsed = {}
    return parsed


def inventory_my_snmp_check(parsed):
    return []


def check_my_snmp_check(item, params, parsed):
    return 3, 'not yet implemented'


check_info['my_snmp_check'] = {
    'parse_function'        : parse_my_snmp_check,
    'inventory_function'    : inventory_my_snmp_check,
    'check_function'        : check_my_snmp_check,
    'service_description'   : 'DESCR',
    'snmp_info'             : ('', []),
    'snmp_scan_function'    : lambda oid: False,
}

figheader

Figheader ist ein Helper der Kommentarblöcke im Check_MK Style erzeugt. figlet muss auf dem System installiert sein.

Zu finden unter ~/share/doc/check_mk/helpers/figheader

OMD[test1]:~$ ~/share/doc/check_mk/helpers/figheader whoohoo
#.
#   .--whoohoo-------------------------------------------------------------.
#   |                     _                 _                              |
#   |           __      _| |__   ___   ___ | |__   ___   ___               |
#   |           \ \ /\ / / '_ \ / _ \ / _ \| '_ \ / _ \ / _ \              |
#   |            \ V  V /| | | | (_) | (_) | | | | (_) | (_) |             |
#   |             \_/\_/ |_| |_|\___/ \___/|_| |_|\___/ \___/              |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

wato_import.py

Wato_import.py ist ein Script aus der Schatzkiste und dient zum Import einer CSV Liste in WATO.

Zu finden unter ~/share/doc/check_mk/treasures/wato_import.py

OMD[test1]:~/share/doc/check_mk/treasures$ python wato_import.py 
Run this script inside a OMD site
    Usage: ./wato_import.py csvfile.csv
    CSV Example:
    wato_foldername;hostname|tag1 tag2;host_alias;ipaddress|None

cmk-delete-host

Cmk-delete-host ist ebenfalls aus der Schatzkiste und dient zum Löschen von Hosts von der Kommandozeile aus. Dazu wird die Automation Schnittstelle verwendet.

Zu finden unter ~/share/doc/check_mk/treasures/cmk-delete-host

extract_inventory.py

Extract_inventory.py ist aus der Schatzkiste und exportiert die Inventory Daten als CSV.

Zu finden unter ~/share/doc/check_mk/treasures/inventory/extract_inventory.py

OMD[test1]:~/share/doc/check_mk/treasures/inventory$ ./extract_inventory.py 
creating relation inv_raw_generic(OS) for localhost
creating relation inv_raw_generic(Linux) for localhost
creating relation inv_raw_arp for localhost
creating relation devices for localhost
creating relation inv_raw_file for localhost

mmm@max:/var/tmp# ls -la
insgesamt 32
drwxrwxrwt  3 root  root  4096 Jul 14 11:41 .
drwxr-xr-x 13 root  root  4096 Feb  3  2014 ..
-rw-r--r--  1 test1 test1  377 Jul 14 11:41 devices
-rw-r--r--  1 test1 test1   75 Jul 14 11:41 inv_raw_arp
-rw-r--r--  1 test1 test1  114 Jul 14 11:41 inv_raw_file
-rw-r--r--  1 test1 test1   59 Jul 14 11:41 inv_raw_generic(Linux)
-rw-r--r--  1 test1 test1   43 Jul 14 11:41 inv_raw_generic(OS)

mmm@max:/var/tmp# cat devices 
"import_id", "import_data_source_id", "import_org_level_2_id", "device_key", "device_manufacturer", "device_model", "serial_number", "operating_system", "inventory_date", "installation_date", "cpu_socket_count", "cpu_chip_count", "cpu_core_count", "cpu_speed", "cpu_name"
"localhost", "sla", "default", "localhost", "", "", "", "", "2017-07-14", "", "", "1", "4", "3200.0", ""

Inoffizielle Notification Scripts

In den Treasures befinden sich noch einige Notification Scripts welche man gut als Vorlage für eigene Scripte verwenden kann. Eigene Notification Scripte sind unter ~/local/share/check_mk/notifications abzulegen.

OMD[test1]:~/share/doc/check_mk/treasures/notifications$ ls -la
insgesamt 64
drwxr-xr-x  2 root root  4096 Jul 11 13:55 ./
drwxr-xr-x 21 root root  4096 Jul 11 13:55 ../
-rwxr-xr-x  1 root root  2829 Okt 12  2016 braintower*
-rwxr-xr-x  1 root root 24497 Jul  5 14:53 glpi.py*
-rwxr-xr-x  1 root root  3842 Okt 12  2016 mobilant*
-rwxr-xr-x  1 root root  5114 Okt 12  2016 multitech*
-rwxr-xr-x  1 root root  2059 Okt 12  2016 opcmsg*
-rw-r--r--  1 root root   344 Okt 12  2016 README
-rwxr-xr-x  1 root root   632 Okt 12  2016 slack*
-rwxr-xr-x  1 root root   803 Okt 12  2016 snmp_trap*

Andere Dinge

In der Schatzkiste und in vielen Ordnern im Check_MK Root verbergen sich noch viele coole Sachen am besten geht ihr mal selbst auf die Suche 🙂

~/share/doc/check_mk/treasures

~/share/doc/check_mk/helpers

Check_MK: Eigener Agent Check

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:

 

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 🙂

 

Check_MK: Einfacher lokaler Check (Agent)

Dem Check_MK Agenten ist es egal mit welcher Sprache ein lokaler Check geschrieben ist, er muss vom Betriebsystem ausführbar sein. Somit kann jede Scriptsprache (Perl, PHP, Python, Powershell, VBS, etc.) verwendet werden. Der Agent erwartet folgendes Format:

<checkresult> <name> <perfdata> <status_text>

Checkresult

0 = OK – 1 = Warning – 2 = Crtitcal – 3 = Unknown

Name

Dies Repräsentiert den Namen des Checks, whitespaces sind durch Unterstriche zu ersetzen, z.B. DNS_Check

Perfdata

Wenn von dem Check keine Performancedaten erzeugt werden ist hier ein Minus – einzusetzen; Die Werte sind in folgendem Format zu übergeben:

varname=value;warn;crit;min;max

Min und Max sind nur für das alte PNP4Nagios bei der freien Check_MK Version, das neue Graphing System der Enterprise Version benötigt das nicht und können weggelassen werden.

Mehrere Performancewerte werden durch ein Pipe | getrennt. z.B.

varname=value;warn;crit|varname2=value;warn;crit|varname3=value;warn;crit

Checks z.B. nur alle 10 Minuten ausführen

Per default wird der lokale Check jedes mal ausgeführt wenn Check_MK den Agenten abfrägt, das Verhalten kann man manipulieren in dem man im local Ordner einen Ordner anlegt in Sekunden, z.B. 10 Minuten sind 600 Sekunden. Der Check mein_check.sh wird dann im Ordner local/600/mein_check.sh abgelegt. Das Checkergebnis wird für die Anzahl der Sekunden des Ordners gecached.

Die Local Checks liegen in einem Linux System unter /usr/lib/check_mk_agent/local, in Windows ist es abhänig vom Installationsort. Diesen kann man ermitteln indem man den Agenten per Telnet abfrägt:

telnet 1.2.3.4 6556
<<<check_mk>>>
...
LocalDirectory: C:\program files\check_mk_agent\local
...

Bei Windows Maschinen muss noch in der check_mk.ini definiert werden welche Scriptendungen ausgeführt werden dürfen.

# Example configuration for Windows agent
[global]
    # Restrict access to certain IP addresses
    # If ipv6 is enabled, all listed ipv4 adresses are also accepted as
    # source adresses in their ipv6-mapped form. I.e. if
    # 192.168.56.0/24 is listed, connections from ::ffff:c0a8:3800/120
    # are also possible
    # only_from = 127.0.0.1 192.168.56.0/24 ::1

    # Change port where the agent is listening ( default 6556 )
    # port = 6556

    # Disable ipv6 support. By default, ipv4 is always supported
    # and ipv6 is enabled additionally if supported by the system.
    # ipv6 = no

    # Do only execute programs and scripts with
    # the following suffixes (in local and plugins).
    # Per default all extensions except txt and dir
    # are being executed.
    execute = exe bat vbs ps1

Zu finden ist ein Beispiel INI File unter /omd/versions/default/share/check_mk/agents/windows/check_mk.example.ini

Einfacher lokaler Check als Python Script:

#!/usr/bin/env python

import os

warn = 100000
crit = 200000

def size(path = '.'):
    tsize = 0
    for dp, dn, fn in os.walk(path):
        for f in fn:
            fp = os.path.join(dp, f)
            tsize += os.path.getsize(fp)
    return tsize

tmp_size = size("/tmp")

if tmp_size >= crit:
    print "2 tmp dir size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)
elif tmp_size >= warn:
    print "1 tmp_dir_size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)
else:
    print "0 tmp_dir_size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)



Ein weiteres tolles Feature der lokalen Checks ist das man sich theoretisch nicht um die Logik kümmern muss damit OK, WARN oder CRIT zurückgegeben wird. Dazu müssen bei dem Counter Warning und Critical Werte mitgegeben werden. Dazu gibt es ein schönes Beispiel in den „Treasures“ von Check_MK (siehe localchecks/zombies).

Die Treasures sind zu finden unter: /omd/versions/default/share/doc/check_mk/treasures

Hier ein Beispiel:

#!/bin/bash

proc=$( ps aux | wc -l )

echo "P Processes processes=$proc;1000;5000"

Mit P am Anfang anstatt 0,1,2,3 wird der Status von Check_MK bestimmt.

Andere Beispiele, etc. bei Mathias Kettner direkt und weitere Infos: (siehe z.B. filecount) https://mathias-kettner.de/checkmk_localchecks.html