From 7ba061fef497f7a166f73ed12261e128acc3cbe4 Mon Sep 17 00:00:00 2001 From: Silke Paintner Date: Wed, 31 Jul 2024 09:17:51 +0200 Subject: [PATCH] removed unneccessary draft --- de/draft_devel_check_plugins.asciidoc | 3166 ------------------------- 1 file changed, 3166 deletions(-) delete mode 100644 de/draft_devel_check_plugins.asciidoc diff --git a/de/draft_devel_check_plugins.asciidoc b/de/draft_devel_check_plugins.asciidoc deleted file mode 100644 index 5bafbc701..000000000 --- a/de/draft_devel_check_plugins.asciidoc +++ /dev/null @@ -1,3166 +0,0 @@ -// -*- coding: utf-8 -*- -// IGNORE % Benachrichtigungs greppen check_mk lq -include::global_attr.adoc[] -= Eigene Check-Plugins schreiben -:revdate: draft -:title: Eigene Check-Plugins schreiben -:description: Hier erfahren Sie, wie Sie {CMK}-Plugins entwickeln können - mit allem was dazugehört, insbesondere mit der in der Version 2.0.0 neu entwickelten Check-API. - -{related-start} -xref:localchecks#[Lokale Checks] -xref:wato_monitoringagents#[Monitoring-Agenten] -xref:agent_linux#[Linux überwachen] -xref:agent_windows#[Windows überwachen] -xref:snmp#[Überwachen via SNMP] -xref:wato_services#[Services verstehen und konfigurieren] -xref:cmk_commandline#[{CMK} auf der Kommandozeile] -xref:mkps#[{CMK}-Erweiterungspakete (MKPs)] -xref:simulation_mode#[Der Simulationsmodus] -{related-end} - - -[#intro] -== Einleitung -// TK: Das Kapitel ist für mich - -Check-Plugins sind in Python geschriebene Software-Module, die auf der {CMK}-xref:glossar#site[Instanz] ausgeführt werden und die xref:glossar#service[Services] eines xref:glossar#host[Hosts] erstellen und auswerten. - -{CMK} umfasst über 2 000 fertige Check-Plugins für alle nur denkbare Hardware und Software. -Diese werden vom {CMK}-Team gepflegt, und jede Woche kommen neue dazu. -Daneben gibt es auf der link:https://exchange.checkmk.com[{CMK} Exchange^] weitere Plugins, die von unseren Anwendern beigesteuert werden. - -Und trotzdem kann es immer wieder vorkommen, dass ein Gerät, eine Anwendung oder einfach nur eine bestimmte xref:glossar#metric[Metrik,] die für Sie wichtig ist, noch von keinem dieser Plugins erfasst ist --- vielleicht auch einfach deshalb, weil es sich dabei um etwas handelt, dass in Ihrer Firma entwickelt wurde und es daher niemand anders haben kann. - -// [#real_plug-in] -// === Muss es immer ein Check-Plugin sein? -Die gute Nachricht ist: -Sie können sich selbst helfen und für Ihr ganz spezielles „Ding“ eine sinnvolle Überwachung selbst implementieren. -Dabei haben Sie zuerst einmal vier Möglichkeiten: - -[cols=4,options="header"] -|=== -|Methode -|So geht's -|Vorteile -|Nachteile - -|xref:glossar#local_check[Lokaler Check] -|{CMK}-Agent um ein einfaches Skript erweitern. -|Geht sehr einfach, ist in allen Programmiersprachen möglich, welche das Betriebssystem des überwachten Hosts anbietet, und unterstützt sogar die xref:glossar#service_discovery[Service-Erkennung]. -|Konfiguration der Schwellwerte nur beim Agenten selbst, für komplexere Dinge unkomfortabel und für xref:snmp#[SNMP] gar nicht möglich. - -|Legacy Nagios Check-Plugin -|Plugin per _MRPE_ vom xref:agent_windows.html#mrpe[Windows-] oder xref:agent_linux#mrpe[Linux-]Agenten aufrufen lassen. -|Zugriff auf alle vorhandenen Nagios-Plugins und freie Wahl der Programmiersprache. -|Konfiguration der Schwellwerte nur beim Agenten selbst, keine SNMP-Unterstützung durch {CMK} und keine Service-Erkennung möglich. - -|Auswertung von Log-Meldungen -|Meldungen überwachen mit der xref:glossar#ec[Event Console]. -|Keine Entwicklung notwendig, sondern nur die Erstellung von Regeln in der Event Console. -|Geht nur, wenn passende Log-Meldungen vorhanden sind. Es gibt keinen gesicherten aktuellen Status, keine Erfassung von Metriken und keine konfigurierbaren Schwellwerte. - -|Echtes {CMK} Check-Plugin -|Wird in diesem Artikel erklärt. -|Ist sehr performant und fügt sich zu 100{nbsp}% in {CMK} ein mit automatischer Service-Erkennung, zentraler Konfiguration der Schwellwerte über die grafische Oberfläche, automatischer Erstellung von xref:glossar#label[Labels] etc. -Unterstützt werden SNMP und die xref:inventory#[HW/SW-Inventur]. -{CMK} selbst unterstützt die Erstellung von Check-Plugins mit Standardbibliotheken. -|Bietet das volle Programm und alle Möglichkeiten, erfordert aber mehr Einarbeitungszeit sowie Kenntnisse in der Programmiersprache Python. -|=== - -Dieser Artikel zeigt Ihnen, wie Sie echte {CMK} Check-Plugins entwickeln können -- mit allem was dazugehört. -Für die Programmierung der Check-Plugins gibt es seit der {CMK}-Version {v20} eine neu entwickelte *Check-API.* -Wir zeigen Ihnen, wie Sie diese Check-API für die Plugin-Programmierung nutzen können. - - -[#check_api_doc] -=== Die Check-API-Dokumentation - -Über die {CMK}-Benutzeroberfläche haben Sie jederzeit Zugriff auf die Dokumentation der Check-API: mit [.guihint]#Help > Developer resources > Check plugin API reference.# -Wählen Sie im neuen Browserfenster in der linken Navigationsleiste [.guihint]#BASE > Agent based API ("Check API")# aus: - -[{image-border}] -image::devel_cpi_checkapi_doc.png[alt="Seite zum Einstieg in die Check-API-Dokumentation."] - -*Hinweis:* Falls Sie bereits Check-Plugins entwickelt haben mit der alten API, die bis zur Version {v16} gültig war, und die Plugins noch nicht auf die neue Check-API migriert haben, -so finden Sie im xref:check_api_old[Anhang] Informationen zu den Unterschieden zwischen alter und neuer Check-API. - - -=== Verschiedene Arten von Agenten - -Check-Plugins werten die Daten aus, die xref:glossar#agent[Monitoring-Agenten] bereitstellen. -Bevor wir uns ins Geschehen stürzen, zeigen wir deshalb zunächst, welche Arten von Agenten {CMK} eigentlich kennt. - -[cols="20,~",options="header"] -|=== -|Agent |Beschreibung - -|{CMK}-Agent -|Hier werten das Check-Plugin Daten aus, die der {CMK}-Agent für Linux, Windows oder andere Betriebssysteme sendet. -Damit werden Betriebssystemparameter und Anwendungen überwacht und teilweise auch Server-Hardware. -Jedes neue Check-Plugin erfordert eine Erweiterung des Agenten in Form eines xref:glossar#agent_plugin[Agentenplugins,] damit der Agent auch die Daten bereitstellt, die die Checks benötigen. - -|SNMP-Agent -|Bei der Überwachung via xref:snmp#[SNMP] benötigen Sie keine Erweiterung eines Agenten. -Stattdessen wertet das Check-Plugin Daten aus, die {CMK} von dem zu überwachenden Gerät per SNMP abruft, und die der auf dem Gerät vorinstallierte SNMP-Agent standardmäßig bereitstellt. -{CMK} unterstützt Sie dabei und übernimmt sämtliche Details und Sonderheiten des SNMP-Protokolls. -// Eigentlich gibt es auch hier einen Agenten: nämlich den auf dem überwachten System vorinstallierten SNMP-Agenten. - -|Spezialagent / API-Integration | -Einen Spezialagenten benötigen Sie, wenn Sie weder mit dem normalen {CMK}-Agenten noch per SNMP an die Daten kommen, welche für das Monitoring relevant sind. -Der häufigste Fall ist das Abfragen von HTTP-basierten APIs. -Beispiele sind die Überwachung von xref:monitoring_aws#[AWS], xref:monitoring_azure#[Azure] oder xref:monitoring_vmware#[VMware]. -Hier schreiben Sie ein Skript, welches direkt auf dem {CMK}-Server läuft, sich mit der API verbindet, und Daten im gleichen Format ausgibt, wie dies ein Agentenplugin tun würde. - -|Aktiver Check -|Dieser „Agent“ passt nicht so ganz in die Reihe und hat eine Sonderrolle. -Ein aktiver Check ist ein Nagios-kompatibles Plugin, welches für die Ausführung _auf dem {CMK}-Server_ bestimmt ist und von dort aus mit einem Netzwerkprotokoll direkt einen Dienst auf dem Zielgerät abfragt. -Der aktive Check kümmert sich nicht nur um die Auswertung der Daten, sondern auch um deren Erhebung -- und passt so gesehen doch wieder in die Reihe, weil er das tut, was sonst die Aufgabe des Agenten ist: Daten liefern. + -Das prominenteste Beispiel ist das Plugin `check_http`, mit welchem Sie Webserver und Webseiten überwachen können. -Dieses Plugin können Sie dann so in {CMK} integrieren, dass man es wie gewohnt per Regeln einrichten kann. -|=== -// COMMENT[Artikel zu Spezialagent und Aktiven Check muss geschrieben werden.] - -Sie sehen, dass es viele Möglichkeiten gibt, an die Daten zu kommen, die für die Auswertung in {CMK} relevant sind. -In diesem Artikel behandeln wir Check-Plugins, die Daten auswerten, die von {CMK}-Agenten und SNMP-Agenten zur Verfügung gestellt werden. - - -=== Voraussetzungen - -Wenn Sie Lust haben, sich mit dem Programmieren von Check-Plugins zu befassen, -benötigen Sie folgendes: - -* Kenntnisse in der Programmiersprache Python -* Erfahrung mit {CMK}, vor allem was das Thema Agenten und Checks betrifft -* etwas Übung mit Linux auf der Kommandozeile - - -[#first_check_plugin] -== Ein erstes Check-Plugin schreiben - -Wer sich für die Plugin-Programierung in {CMK} interessiert, hat mit großer Wahrscheinlichkeit auch schon einmal einen {CMK}-Server aufgesetzt. -Haben auch Sie dies bereits gemacht, haben Sie dabei als Einstieg vermutlich auch Ihren {CMK}-Server als Host überwacht. -{CMK}-Server und Host sind auch im nachfolgenden Beispiel identisch. -Dadurch können für die Abfrage von Informationen Livestatus-Abfragen über den Host genutzt werden. - -Im folgenden Beispiel - einer einfachen Überwachung innerhalb von {CMK} - gehen wir von einem Unternehmen mit mehreren Standorten aus. -Jeder dieser Standorte wird in {CMK} durch eine xref:glossar#host_group[Host-Gruppe] abgebildet. -Jeder Standort hat sein eigenes Service-Team. -Damit bei Problemen jeweils das richtige Service-Team verständigt werden kann, gilt, dass jeder Host einem Standort - also auch einer Host-Gruppe - zugewiesen sein muss. -Ziel dieses Beispiels ist nun, eine Prüfung aufzusetzen mit der kontrolliert werden kann, dass für keinen Host vergessen wurde, eine Host-Gruppe zuzuweisen. - -Das Ganze läuft in zwei Schritten: - -. Heraussuchen des korrekten Befehls, um die notwendigen Informationen zu erhalten. -. Schreiben eines Check-Plugins in der {CMK}-Instanz, welches diese Daten auswertet. - -Und los geht's... - - -[#right_command] -=== Den richtigen Befehl finden - -Am Anfang jeder Check-Programmierung steht: die Recherche! -Das bedeutet, dass Sie herausfinden müssen, wie Sie überhaupt an die Informationen kommen, die Sie für die Überwachung brauchen. -Bei Linux sind das oft Kommandozeilenbefehle, bei Windows hilft die PowerShell, VBScript oder WMI und bei xref:snmp#[SNMP] müssen Sie die richtigen OIDs finden (dazu gibt es ein xref:snmp[eigenes Kapitel]). - -Für das Herausfinden des richtigen Befehls gibt es leider kein allgemeines Vorgehen. -Das jeweils passende Kommando hängt von der jeweiligen Aufgabenstellung ab. - -Für das gewählte Beispiel nutzen wir die Tatsache, dass der {CMK}-Server zugleich der Host ist. Damit genügt zunächst ein xref:livestatus#[Abruf der Statusdaten via Livestatus,] also der in Tabellen organisierten Daten, die {CMK} über die überwachten Hosts und Services hat. - -Dazu dient der `lq`-Befehl in Kombination mit einem `GET`-Befehl. -`lq` steht für _Livestatus Query_ und aktiviert die Livestatus-Abfrage in {CMK}, das nachfolgende `GET` holt die dahinter spezifizierten Informationen ab. - -Öffnen Sie eine Kommandozeile für Ihre {CMK}-Instanz. Melden Sie sich als Instanzbenutzer an und fragen Sie die Informationen zu den Host-Gruppen ab: - -[{shell}] ----- -{c-omd} lq "GET hostgroups" -action_url;alias;members;members_with_state;name;notes;notes_url;num_hosts;num_hosts_down;num_hosts_handled_problems;num_hosts_pending;num_hosts_unhandled_problems;num_hosts_unreach;num_hosts_up;num_services;num_services_crit;num_services_handled_problems;num_services_hard_crit;num_services_hard_ok;num_services_hard_unknown;num_services_hard_warn;num_services_ok;num_services_pending;num_services_unhandled_problems;num_services_unknown;num_services_warn;worst_host_state;worst_service_hard_state;worst_service_state -;Munich;myhost3,myhost2,myhost1;myhost3|1|1,myhost2|1|1,myhost1|1|1;Munich;;;3;3;0;0;3;0;0;6;6;0;6;0;0;0;0;0;6;0;0;1;2;2 -;Hamburg;myhost22,myhost33,myhost11;myhost22|1|1,myhost33|1|1,myhost11|1|1;Hamburg;;;3;3;0;0;3;0;0;6;6;0;6;0;0;0;0;0;6;0;0;1;2;2 -;check_mk;SQL-Server,localhost;SQL-Server|1|1,localhost|0|1;check_mk;;;2;1;0;0;1;0;1;60;6;0;6;51;0;3;51;0;9;0;3;1;2;2 ----- - -Die erste Zeile der Ausgabe enthält die Spaltennamen der abgefragten Tabelle `hostgroups`. - -Als Trennzeichen fungiert das Semikolon. -In der zweiten Zeile folgen dann die Inhalte sämtlicher Spalten, ebenfalls durch Semikolons getrennt. - -Die Ausgabe ist bereits für dieses kleine Beispiel relativ unübersichtlich und enthält Informationen, die für uns aktuell nicht relevant sind. -Also schränken Sie die Abfrage ein - auf die Namen der Host-Gruppen und die in den Gruppen befindlichen Hosts: - -[{shell}] ----- -{c-omd} lq "GET hostgroups\nColumns: name members" -Munich;myhost3,myhost2,myhost1 -Hamburg;myhost22,myhost33,myhost11 -check_mk;SQL-Server,localhost ----- - -Die Livestatus-Schnittstelle erwartet alle Befehle und Header in jeweils einer eigenen Zeile. Um die daher notwendigen Zeilenumbrüche kenntlich zu machen, trennen Sie die einzelnen Eingabeteile mit einem `\n`. - -In diesem Beispiel existieren aktuell drei Host-Gruppen, zwei Niederlassungs-Gruppen (die bereits früher angelegt wurden) sowie die Gruppe `check_mk`. -Diese enthält zwei Hosts namens `SQL-Server` und `localhost`. - -Diese Host-Gruppe `check_mk` stellt eine Besonderheit innerhalb der Host-Gruppen dar. -Sie haben diese nämlich nicht selbst angelegt. -Und Sie können auch keinen Host aktiv in diese Gruppe aufnehmen. -Woher also stammt diese Host-Gruppe? -Da in {CMK} per Definition jeder Host einer Gruppe angehören muss, weist {CMK} jeden Host, den Sie keiner Gruppe zuweisen, der "speziellen" Gruppe `check_mk` zu. - -Sobald Sie einen Host einer Ihrer eigenen Host-Gruppen zuweisen, wird er aus der Gruppe `check_mk` entfernt. -Ein Host, der von {CMK} der Host-Gruppe `check_mk` zugewiesen wurde, kann also, im Gegensatz zu einem Host, der sich in einer Ihrer selbst angelegten Gruppen befindet, keiner weiteren Host-Gruppe zugewiesen werden. Auch gibt es keinen Weg, einen Host bewusst der Host-Gruppe `check_mk` zuzuweisen. - -Genau diese Eigenschaften der Gruppe `check_mk` werden nun für unser Beispiel genutzt. -Da jeder Host einer Niederlassung zugeordnet sein soll, müsste die Host-Gruppe `check_mk` leer sein. -Ist sie nicht leer, so besteht Handlungsbedarf, sprich darin befindliche Hosts müssen den Host-Gruppen und damit den Niederlassungen zugeordnet werden. - - -[#command_in_agent] -=== Den Befehl in den Agenten einbauen - -Bis jetzt haben Sie sich mit dem `lq`-Befehl auf der Kommandozeile die Informationen nur anzeigen lassen. -Das ist hilfreich, um sich einen Einblick in die Daten zu verschaffen. - -Damit Sie diese Daten vom {CMK}-Server aus abrufen können, muss der neue Befehl Teil vom {CMK}-Agenten auf dem überwachten Host werden. -Sie könnten nun direkt den {CMK}-Agenten in der Datei `/usr/bin/check_mk_agent` editieren und das einbauen. -Das hätte aber den Nachteil, dass Ihr neuer Befehl bei einem Software-Update des Agenten wieder verschwindet, weil die Datei ersetzt wird. - -Besser ist es daher, ein xref:glossar#agent_plugin[*Agentenplugin*] zu erstellen. -Alles was Sie dafür brauchen, ist eine ausführbare Datei, die den Befehl enthält und im Verzeichnis `/usr/lib/check_mk_agent/plugins` liegt. - -Und noch eins ist wichtig: -Die Daten können nicht einfach so ausgegeben werden. -Sie brauchen noch einen *Sektions-Header* (_section header_). -Das ist eine speziell formatierte Zeile, in der der Name des neuen Agentenplugin steht. -// TK: Ich find das voll verwirrend, wie hier Agentenplugin, Sektions-Header, Check und Check-Plugin durcheinandergehen. -An diesem Sektions-Header kann {CMK} später erkennen, wo die Daten des Check-Plugins beginnen und die des vorherigen aufhören. Am einfachsten ist es, wenn Sektions-Header und Check-Plugin den gleichen Namen tragen - auch wenn dies nicht verpflichtend ist. - -Also brauchen Sie jetzt erst einmal einen sinnvollen Namen für Ihr neues Check-Plugin. -Dieser Name darf nur Kleinbuchstaben (nur _a-z_, keine Umlaute, keine Akzente), Unterstriche und Ziffern enthalten und muss eindeutig sein. -Vermeiden Sie Namenskollisionen mit vorhandenen Check-Plugins. -Wenn Sie neugierig sind, welche Namen es schon gibt, können Sie diese in einer {CMK}-Instanz auf der Kommandozeile mit `cmk -L` auflisten lassen: - -[{shell}] ----- -{c-omd} cmk -L | head -n 20 -3par_capacity agent HPE 3PAR: Capacity -3par_cpgs agent HPE 3PAR: CPGs -3par_cpgs_usage agent HPE 3PAR: CPGs Usage -3par_hosts agent HPE 3PAR: Hosts -3par_ports agent HPE 3PAR: Ports -3par_remotecopy agent HPE 3PAR: Remote Copy -3par_system agent HPE 3PAR: System -3par_volumes agent HPE 3PAR: Volumes -3ware_disks agent 3ware ATA RAID Controller: State of Disks -3ware_info agent 3ware ATA RAID Controller: General Information -3ware_units agent 3ware ATA RAID Controller: State of Units -acme_agent_sessions snmp ACME Devices: Agent Sessions -acme_certificates snmp ACME Devices: Certificates -acme_fan snmp ACME Devices: Fans -acme_powersupply snmp ACME Devices: Power Supplies -acme_realm snmp ACME Devices: Realm -acme_sbc agent ACME SBC: Health -acme_sbc_settings agent ACME SBC: Health Settings -acme_sbc_snmp snmp ACME SBC: Health (via SNMP) -acme_temp snmp ACME Devices: Temperature ----- - -Die zweite Spalte zeigt an, wie das jeweilige Check-Plugin seine Daten bezieht. - -Ein eindeutiger Name für das neue Check-Plugin, der alle Kriterien erfüllt, ist zum Beispiel `myhostgroups`. - -Nun haben Sie alle Informationen zusammen, um das Skript mit dem Agentenplugin zu erstellen. Legen Sie eine neue Datei `myhostgroups` im Verzeichnis `/usr/lib/check_mk_agent/plugins` an: - -./usr/lib/check_mk_agent/plugins/myhostgroups -[{file}] ----- -#!/bin/bash - -COLUMNS="name members" -SITE="mysite" - -echo "<<>>" -echo -e "GET hostgroups\nColumns: $COLUMNS" | /omd/sites/$SITE/bin/unixcat /omd/sites/$SITE/tmp/run/live ----- - -Aber was bedeutet das nun alles im Einzelnen? -Die erste Zeile enthält den „Shebang“ (das ist eine Abkürzung für _sharp_ und _bang_, wobei letzteres eine Abkürzung für das Ausrufezeichen ist), an dem Linux erkennt, dass es das Skript mit der Shell ausführen soll: `#!/bin/bash` - -Um das Skript erweiterbar zu halten, werden als nächstes zwei Variablen eingeführt: - -* die Variable `COLUMNS`, die aktuell die Gruppennamen und die zugehörigen Mitglieder enthält - -* die Variable `SITE`, die den Namen der {CMK}-Instanz enthält - -Mit dem ersten echo-Befehl geben Sie den Sektions-Header aus. -Da die Spalten der Tabelle mit dem Semikolon getrennt werden, legen Sie gleichzeitig mit dem Zusatz `sep(59)` fest, dass das Semikolon als Trennzeichen (_separator_) für die Daten in der Agentenausgabe genutzt wird. -59 steht für das ASCII-Zeichen Nummer 59, also das Semikolon. -Ohne diesen Zusatz würde als Standard das Leerzeichen (ASCII-Zeichen 32) als Trennzeichen verwendet werden. - -Mit dem zweiten echo-Befehl wird das `lq`-Kommando, das Ihnen als Instanzbenutzer zur Verfügung steht, ersetzt, so dass es in einem Skript funktioniert, dass vom `root`-Benutzer ausgeführt wird. -Mit `echo` wird dabei die Ausgabe des `GET`-Kommandos an das Kommando `unixcat` weitergeleitet. Der Unix-Socket `~/tmp/run/live` wird dahinter als Ziel der Ausgabe definiert. -Dabei sorgt die Option `-e` hinter dem `echo` dafür, dass das `\n` im `GET`-Kommando korrekt interpretiert wird. - -Eines ist noch sehr wichtig, sobald Sie die Datei erstellt haben: -Machen Sie die Datei ausführbar. - -[{shell}] ----- -{c-root} chmod +x /usr/lib/check_mk_agent/plugins/myhostgroups ----- - -Sie können das Agentenplugin direkt von Hand ausprobieren, indem Sie den kompletten Pfad als Befehl eingeben: - -[{shell-raw}] ----- -{c-root} /usr/lib/check_mk_agent/plugins/myhostgroups -<<>> -Munich;myhost3,myhost2,myhost1 -Hamburg;myhost22,myhost33,myhost11 -check_mk;SQL-Server,localhost ----- - - -[#test_agent] -=== Agent ausprobieren - -Test und Fehlersuche sind am wichtigsten, um zu einem funktionierenden Agentenplugin zu gelangen. -Am besten gehen Sie in drei Schritten vor: - -. Agentenplugin solo ausprobieren. -Das haben Sie gerade gemacht. - -. Agent als ganzes lokal testen. - -. Agent vom {CMK}-Server aus abrufen. - -Das lokale Testen des Agenten ist sehr einfach. -Rufen Sie als `root` den Befehl `check_mk_agent` auf. - -[{shell}] ----- -{c-root} check_mk_agent ----- - -Irgendwo in der Ausgabe muss die neue Sektion erscheinen. - -Hier ist ein Ausschnitt der Ausgabe, welcher diese enthält: - - -[{shell-raw},highlight=2-3] ----- -<<>> -<<>> -Munich;myhost3,myhost2,myhost1 -Hamburg;myhost22,myhost33,myhost11 -check_mk;SQL-Server,localhost -<<>> -AgentUpdate: last_check 1665481638.3074498 last_update None aghash None pending_hash ----- - -Durch Anhängen von `less` können Sie in der Ausgabe blättern (drücken Sie die Leertaste zum Blättern, `/` zum Suchen und `Q` zum Beenden): - -[{shell}] ----- -{c-root} check_mk_agent | less ----- - -Der dritte Test ist dann direkt von der {CMK}-Instanz aus. -Nehmen Sie den Host ins Monitoring auf (z.B. als `localhost`) und rufen Sie die Agentendaten dann mit `cmk -d` ab. - -[{shell}] ----- -{c-omd} cmk -d localhost | less ----- - -Hier sollte die gleiche Ausgabe kommen wie beim vorherigen Kommando. - -Übrigens: -`grep` hat mit `-A` eine Option, nach jedem Treffer noch einige Zeilen mehr auszugeben. -Damit können Sie bequem die Sektion suchen und ausgeben: - -[{shell-raw}] ----- -{c-omd} cmk -d localhost | grep -A5 '^<<>> -Munich;myhost3,myhost2,myhost1 -Hamburg;myhost22,myhost33,myhost11 -check_mk;SQL-Server,localhost -<<>> -AgentUpdate: last_check 1665481638.3074498 ----- - -Wenn das funktioniert, ist Ihr Agent vorbereitet! -Und was haben Sie dafür gemacht? -Sie haben ein kurzes Skript mit dem Pfad `/usr/lib/check_mk_agent/plugins/myhostgroups` erzeugt und ausführbar gemacht! - -Alles was nun folgt, geschieht nur noch auf dem {CMK}-Server: -Dort schreiben Sie das eigentliche Check-Plugin. - - -[#write_checkplugin] -=== Das Check-Plugin schreiben - -Das Vorbereiten des Agenten ist zwar der komplizierteste Teil, aber nur die -halbe Miete. Jetzt müssen Sie {CMK} noch beibringen, wie es mit den Informationen -und der neuen Agentensektion umgehen soll, welche Services es erzeugen soll, -wann diese auf {OK} oder {CRIT} gehen sollen usw. All dies machen Sie durch -die Programmierung eines Check-Plugins in Python. - -Für Ihre eigenen Check-Plugins finden Sie ein Verzeichnis vorbereitet -in der `local`-Hierarchie des -xref:cmk_commandline#sitedir[Instanzverzeichnisses]. Dieses lautet -`~/local/lib/check_mk/base/plugins/agent_based/`. Hier im Pfad -bedeutet `base` den Teil von {CMK}, der für das eigentlich Monitoring -und die Benachrichtigungen zuständig ist. Das `agent_based` ist -für alle Plugins, die sich auf den {CMK}-Agenten -beziehen (also z.B. nicht Benachrichtigungs-Plugins). Am einfachsten, Sie wechseln -zum Arbeiten dort hinein: - -[{shell}] ----- -{c-omd} cd local/lib/check_mk/base/plugins/agent_based ----- - -Das Verzeichnis gehört dem Instanzbenutzer und ist daher für Sie schreibbar. -Sie können Ihr Plugin mit jedem auf dem Linux-System installierten Texteditor -bearbeiten. - -Legen Sie also unser Beispiel-Plugin hier an. Konvention ist, dass der Dateiname den Namen der Agentensektion wiedergibt. _Pflicht_ ist, dass die Datei mit `.py` endet, denn ab Version {v20} von {CMK} handelt es -sich bei den Plugins immer um echte Python-Module. - -Als erstes müssen Sie die für die Plugins nötigen Funktionen aus -anderen Python-Modulen importieren. Die einfachste Methode dafür ist die -mit einem `pass:[*]`, allerdings wissen Sie dann nie genau welche Informationen Sie nun geholt haben. Für unser Beispiel ist es besser, sich konkret die Funktionen zu holen, die Sie auch nutzen wollen, also : register, Result, Service und State. - -Wie Sie vielleicht ahnen können, steckt hier auch -eine Versionsnummer der API für die Plugin-Programmierung. Diese ist bis -auf weiteres Version 1, was hier durch `v1` abgekürzt ist: - -.~/local/lib/check_mk/base/plugins/agent_based/myhostgroups.py -[{python}] ----- -from .agent_based_api.v1 import register, Result, Service, State ----- - -Diese Versionierung ermöglicht es uns in Zukunft eventuell neue -Versionen der API _parallel_ zu den bisherigen bereitzustellen, -so dass bestehende Check-Plugins weiterhin problemlos funktionieren. - - -[#create_parse] -=== Die Daten mit einer Parse-Funktion aufbereiten - -Als erstes bauen Sie eine Parse-Funktion auf. Diese -hat die Aufgabe, die „rohen“ Agentendaten zu parsen und in eine logisch -aufgeräumte Form zu bringen, die für alle weiteren Schritte einfach -zu verarbeiten ist. Konvention ist, dass diese nach der Agentensektion -benannt wird und mit `parse_` beginnt. Sie bekommt als einziges -Argument `string_table`. Bitte beachten Sie, dass Sie hier nicht -frei in der Wahl des Arguments sind. Es muss wirklich so heißen. - -Eine Ausgabe zu Ihren Host-Gruppen sähe in etwa so aus: - -[{shell}] ----- -[["check_mk", "SQL-Server,localhost"],["Munich", "myhost3,myhost2,myhost1"],["Hamburg", "myhost22,myhost33,myhost11"]] ----- - -Es wird ein einziger langer Datensatz ausgelesen. Jede eckige Klammer enthält die Informationen zu einer Host-Gruppe: erst den Namen der Host-Gruppe in Anführungszeichen, dann dahinter (durch ein Komma abgetrennt) in weiteren Anführungszeichen die zugehörigen Hosts (ebenfalls durch Kommas getrennt). Diese Informationen können Sie zwar alle ansprechen, aber jeweils nur über ihre Position im Datensatz. Bei größeren Datenmengen gestaltet sich dies komplex. - -Die Parse-Funktion soll hier Abhilfe schaffen: - -.local/lib/check_mk/base/plugins/agent_based/myhostgroups.py -[{python}] ----- -from .agent_based_api.v1 import register, Result, Service, State - -def parse_myhostgroups(string_table): - parsed = {} - for line in string_table: - parsed[line[0]] = {"members": line[1]} - return parsed - -register.agent_section( - name = "myhostgroups", - parse_function = parse_myhostgroups, -) ----- - -Mit `def` geben Sie in Python an, dass nachfolgend eine Funktion definiert wird. -Die hier genutzte parse-Funktion (englisch: to parse - analysieren, deutsch: Zerteiler) ist Teil des Compilers, der dafür sorgt, dass der Programmcode korrekt in eine maschinell ausführbare Maschinensprache übersetzt wird. -Die parse-Funktion zerlegt dabei die Daten in ein brauchbares Format für die Weiterverarbeitung. -In unserem Beispiel bedeutet dies, dass jede Zeile durchgegangen wird, beginnend mit Zeile 0. -(Python beginnt mit der Zählung bei Null, d.h. die erste Zeile ist Zeile 0.) -Der Inhalt dieser Zeile wird dann so umgewandelt, dass vorangestellt jeweils die Host-Gruppe steht, gefolgt von jeweils einem Eintrag _members_ mit den "Mitgliedern". - -Mit dem `return parsed` wird diese neue Liste zurückgeben. - -[{shell}] ----- -{"check_mk": {"members": "SQL-Server,localhost"}, "Munich": {"members": "myhost3,myhost2,myhost1"}, "Hamburg": {"members": "myhost22,myhost33,myhost11"}} ----- - - -[#register_check_plug-in] -=== Das Check-Plugin registrieren - -Damit {CMK} weiß, dass es den neuen Check gibt, muss dieser -registriert werden. Dies geschieht durch den Aufruf -der Funktion `register.check_plugin`. -Dabei müssen Sie immer mindestens vier Dinge angeben: - -. `name`: Der Name des Check-Plugins. Wenn Sie keinen Ärger bekommen möchten, nehmen Sie hier den gleichen Namen wie bei Ihrer neuen Agentensektion. Damit weiß der Check automatisch, welche Sektion er auswerten soll. -. `service_name`: Der Name des Services wie er dann im Monitoring erscheinen soll. -. `discovery_function`: Die Funktion zum Erkennen von Services dieses Typs (dazu gleich mehr). -. `check_funktion`: Die Funktion zum Durchführen des eigentlichen Checks (auch dazu gleich mehr). - -Für Ihren Check sieht das dann also so aus: - -[{python}] ----- -register.check_plugin( - name="myhostgroups", - service_name="Hostgroup check_mk", - discovery_function=discover_myhostgroups, - check_function=check_myhostgroups, -) ----- - -Versuchen Sie am besten noch nicht, das gleich auszuprobieren, denn Sie müssen die Funktionen `discover_myhostgroups` und `check_myhostgroups` -vorher noch schreiben. Und diese müssen im Quellcode _vor_ obiger Deklaration -erscheinen. - - -[#discovery_function] -=== Die Discovery-Funktion schreiben - -Eine Besonderheit von {CMK} ist die automatische Erkennung von zu -überwachenden Services. Damit dies klappt, muss jedes Check-Plugin eine -Funktion definieren, welche anhand der Agentenausgaben erkennt, _ob_ ein Service -dieses Typs bzw. _welche_ Services des Typs für den betreffenden Host -angelegt werden sollen. - -Die Discovery-Funktion wird immer dann aufgerufen, wenn für einen Host -die Serviceerkennung durchgeführt wird. Sie entscheidet dann ob, bzw. -welche Services angelegt werden sollen. In Standardfall bekommt sie genau -ein Argument mit dem Namen `section`. Dieses enthält die Daten -der Agentensektion in einem geparsten Format (dazu später mehr). - -Daher implementieren Sie folgende simple Logik: _Wenn_ die Agentensektion -`myhostgroups` vorhanden ist, dann legen Sie auch einen passenden -Service an. Dann erscheint dieser automatisch auf allen Hosts, wo Ihr Agentenplugin ausgerollt ist. Das Vorhandensein der Sektion erkennen Sie ganz einfach daran, dass Ihre Discovery überhaupt aufgerufen wird! - -Die Discovery-Funktion muss für jeden anzulegenden Service mittels -`yield` ein Objekt vom Typ `Service` zurückgeben (nicht mit -`return`). Bei Checks, die pro Host nur einmal auftreten können, -benötigt man keine weitere Angaben: - -[{python}] ----- -def discover_myhostgroups(section): - yield Service() ----- - -[#check_function] -=== Die Check-Funktion schreiben - -Somit können Sie nun zur eigentlichen Check-Funktion kommen, welche anhand -aktueller Agentenausgaben entscheidet, welchen Zustand ein Service -annehmen soll und weitere Informationen ausgeben kann. - -Hier ist die Implementierung: - -[{python}] ----- -def check_myhostgroups(section): - attr = section.get("check_mk") - hosts = attr["members"] if attr else "" - if hosts: - yield Result(state=State.CRIT, summary=f"Default group is not empty; Current member list: {hosts}") - else: - yield Result(state=State.OK, summary="Everything is fine") ----- - -Und nun die Erklärung dazu: -Die Funktion `check_myhostgroups` holt als erstes alle Inhalte aus der Zeile, die mit `check_mk` beginnt in die Variable `attr`. -Dann wird die Variable `hosts` mit dem `members`-Teil der Variable gefüllt, wenn dieser vorhanden ist. -Gibt es keine `members`, so bleibt `hosts` leer. -Jetzt folgt eine If-Abfrage für die eigentliche Auswertung: - -* Enthält die Variable `hosts` Inhalte - ist also die Host-Gruppe `check_mk` nicht leer, so geht der Status des Service auf {CRIT} und es wird ein Hinweistext ausgegeben. Dieser enthält zusätzlich eine Auflistung der Host-Namen aller Hosts, die sich in der Host-Gruppe `check_mk` befinden. - -* Ist die Variable `hosts` leer - sind also keine Hosts (mehr) in der Host-Gruppe `check_mk`, so geht stattdessen der Status des Service auf {OK}. Dann wird zudem ein passender Hinweistext ausgegeben. - - -=== Das ganze Plugin auf einen Blick - -Und hier ist das ganze Plugin nochmal komplett: - -.~/local/lib/check_mk/base/plugins/agent_based/myhostgroups.py -[{python}] ----- -from .agent_based_api.v1 import register, Result, Service, State - -def parse_myhostgroups(string_table): - parsed = {} - for line in string_table: - parsed[line[0]] = {"members": line[1]} - return parsed - -register.agent_section( - name="myhostgroups", - parse_function=parse_myhostgroups, -) - -def discover_myhostgroups(section): - yield Service() - -def check_myhostgroups(section): - attr = section.get("check_mk") - hosts = attr["members"] if attr else "" - if hosts: - yield Result(state=State.CRIT, summary=f"Default group is not empty: {hosts}") - else: - yield Result(state=State.OK, summary="Everything is fine") - -register.check_plugin( - name="myhostgroups", - service_name="Hostgroup check_mk", - discovery_function=discover_myhostgroups, - check_function=check_myhostgroups, -) ----- - -Und das hier war das Plugin für den Linux-Agenten: - -./usr/lib/check_mk_agent/plugins/myhostgroups -[{file}] ----- -#!/bin/bash - -COLUMNS="name members" -SITE="mysite" - -echo "<<>>" -echo -e "GET hostgroups\nColumns: $COLUMNS" | /omd/sites/$SITE/bin/unixcat /omd/sites/$SITE/tmp/run/live ----- - - - -=== Das Plugin in {CMK} testen und aktivieren -//SP: Hier muss noch Text eingefügt werden. - -//// -TK: Ich hab mal zusammengetragen, was ich mir bisher notiert habe: - -cmk_commandline#execute_checks -1) Check-Plugin ausführen: -cmk --detect-plugins=myhostgroups -v localhost - -wato_services.html#commandline -2) Service-Erkennung für das Check-Plugin durchführen: -cmk --detect-plugins=myhostgroups -vI localhost - -cmk_commandline#commands_core -3) Änderungen aktivieren -cmk -O (bei Nagios als Kern cmk -R) - -Das muss man nochmal mit einem frischen Check-Plugin durchspielen, ob es wirklich so und in dieser Reihenfolge funktioniert... -//// - - -[#extend] -== Das Check-Plugin erweitern - - -[#prepare] -=== Vorbereitungen - -Das gerade frisch fertiggestellte erste Check-Plugin soll nun schrittweise erweitert werden. -Bisher hat das Agentenplugin nur die Informationen über die Namen und die Mitglieder der Host-Gruppen geliefert. -Um etwa die Zustände der Hosts und der auf ihnen laufenden Services auswerten zu können, brauchen wir mehr Daten. - - -==== Agentenplugin erweitern - -Wir werden zunächst das Agentenplugin _einmal_ erweitern, um all die Informationen einzusammeln, die für die Erweiterung des Check-Plugins in den nächsten Abschnitten benötigt werden. - -Um herauszubekommen, welche Informationen {CMK} denn für Host-Gruppen so bietet, -können Sie alle verfügbaren Spalten der Host-Gruppentabelle mit dem xref:livestatus#columns[folgenden Kommando] als Instanzbenutzer abfragen: - -[{shell}] ----- -{c-omd} lq "GET columns\nFilter: table = hostgroups\nColumns: name" -action_url -alias -members -members_with_state -name -notes -notes_url -num_hosts -... ----- - -Die Ausgabe geht noch weiter. -Fast 30 Spalten bietet die Tabelle -- und unter den meisten Spaltennamen kann man sich sogar etwas vorstellen. -Uns interessieren die folgenden: -Anzahl der Hosts pro Gruppe (Spalte `num_hosts`), Anzahl der Hosts im Zustand {UP} (`num_hosts_up`), Anzahl der Services aller Hosts in der Gruppe (`num_services`) und Anzahl der Services im Zustand {OK} (`num_services_ok`). - -Jetzt müssen diese neuen Spalten nur noch vom Agenten geliefert werden. -Das erreichen wir durch Erweiterung des im vorherigen Kapitel erstellten xref:command_in_agent[Agentenplugins.] - -Editieren Sie als root-Benutzer das Skript des Agentenplugins. -Da das Skript die konfigurierbaren Werte bereits in Variablen gesteckt hat, reicht es aus, nur die mit `COLUMNS` beginnende Zeile zu ändern und dort die zusätzlichen vier Spalten einzutragen: - -./usr/lib/check_mk_agent/plugins/myhostgroups -[{file},highlight=3] ----- -#!/bin/bash - -COLUMNS="name members num_hosts num_hosts_up num_services num_services_ok" -SITE="mysite" - -echo "<<>>" -echo -e "GET hostgroups\nColumns: $COLUMNS" | /omd/sites/$SITE/bin/unixcat /omd/sites/$SITE/tmp/run/live ----- - -Führen Sie zur Kontrolle das Skript aus: - -[{shell-raw}] ----- -{c-root} /usr/lib/check_mk_agent/plugins/myhostgroups -<<>> -Munich;myhost3,myhost2,myhost1;3;3;180;144 -Hamburg;myhost22,myhost33,myhost11;3;2;132;105 -check_mk;SQL-Server,localhost;2;2;95;83 ----- - -Am Ende jeder Zeile stehen nun, durch Semikolon abgetrennt, die vier neuen Werte. - -Mit dieser Änderung liefert das Agentenplugin nun andere Daten als vorher. -An dieser Stelle ist es wichtig, sich zu vergewissern, dass das Check-Plugin auch mit den geänderten Daten immer noch das tut, was es soll. - - -==== Parse-Funktion erweitern - -Im Check-Plugin ist die Parse-Funktion für die Umwandlung der vom Agentenplugin gelieferten Daten verantwortlich. -Beim xref:create_parse[Erstellen der Parse-Funktion] haben wir nur zwei Spalten der Host-Gruppentabelle berücksichtigt. -Nun werden sechs statt zwei Spalten geliefert. -Die Parse-Funktion muss also fit gemacht werden, um auch die zusätzlichen vier Spalten zu verarbeiten. - -Ändern Sie als Instanzbenutzer die Parse-Funktion in der Datei `myhostgroups.py`, die das Check-Plugin enthält: - -.~/local/lib/check_mk/base/plugins/agent_based/myhostgroups.py -[{python},highlight=4;10-11] ----- -def parse_myhostgroups(string_table): - parsed = {} - for line in string_table: - column_names = ( - "members", - "num_hosts", - "num_hosts_up", - "num_services", - "num_services_ok", - ) - parsed[line[0]] = dict(zip(column_names, line[1:])) - return parsed ----- - -Die markierten Zeilen zeigen, was in der neuen Parse-Funktion verändert wird. -In der `for`-Schleife werden zunächst die zu verarbeitenden Spalten unter ihrem Namen als Variable `column_names` definiert. -Anschließend wird aus der aktuellen Zeile ein Dictionary mit der Python-Funktion `zip` aufgebaut, die im Reißverschlussverfahren aus Spaltennamen und ausgelesenem Wert die Schlüssel-Wert-Paare erzeugt. - -Für die existierende xref:check_function[Check-Funktion] ist diese Erweiterung unkritisch, denn die Datenstruktur für die ersten beiden Spalten bleibt unverändert. -Es werden nur zusätzliche Spalten bereitgestellt, die in der Check-Funktion (noch) gar nicht ausgewertet werden. - -Nun, da die neuen Daten verarbeitet werden können, werden wir sie auch nutzen. - - -[#discovery] -=== Service-Erkennung - -In unserem Beispiel haben wir einen sehr einfachen Check gebaut, der auf einem Host einen Service erzeugt. -Ein sehr üblicher Fall ist aber auch, dass es von einem Check mehrere Services auf einem Host geben kann. - -Das häufigste Beispiel dafür sind die Dateisysteme eines Hosts. -Das Check-Plugin mit dem Namen `df` legt pro Dateisystem auf dem Host einen Service an. -Um diese Services zu unterscheiden, wird der Mount-Punkt des Dateisystems (z.B. `/var`) bzw. der Laufwerksbuchstabe (z.B. `C:`) in den Namen des Services eingebaut. -Das ergibt dann als Service-Name z.B. `Filesystem /var` oder `Filesystem C:`. -Das Wort `/var` bzw. `C:` wird hier als _Item_ bezeichnet. -Wir sprechen also auch von einem _Check mit Items._ - -Wenn Sie einen Check mit Items bauen möchten, müssen Sie folgende Dinge umsetzen: - -* Die Discovery-Funktion muss für jedes der Items, die auf dem Host sinnvollerweise überwacht werden sollen, einen Service generieren. - -* Im Service-Namen müssen Sie das Item mithilfe des Platzhalters `%s` einbauen (also z.B. `"Filesystem %s"`). - -* Die Check-Funktion wird pro Item einmal separat aufgerufen und bekommt dieses als Argument. -Sie muss dann aus den Agentendaten die für dieses Item relevanten Daten herausfischen. - -Um dies praktisch auszuprobieren, werden wir für jede existierende Host-Gruppe einen eigenen Service erzeugen. - -Da das im xref:first_check_plugin[vorherigen Kapitel] erstellte Check-Plugin `myhostgroups` zur Überprüfung der Standardgruppe `check_mk` weiterhin funktionieren soll, bleibt dieses Check-Plugin so, wie es ist. -Für die Erweiterung erstellen wir in der Datei `myhostgroups.py` ein neues Check-Plugin -- im ersten Schritt so wie xref:register_check_plug-in[zuvor] durch die Registrierung des Plugins: - -.~/local/lib/check_mk/base/plugins/agent_based/myhostgroups.py -[{python}] ----- -register.check_plugin( - name="myhostgroups_advanced", - sections=["myhostgroups"], - service_name="Hostgroup %s", - discovery_function=discover_myhostgroups_advanced, - check_function=check_myhostgroups_advanced, -) ----- - -Damit man das neue vom alten Check-Plugin unterscheiden kann, erhält es mit `myhostgroups_advanced` einen eindeutigen Namen. -Der Parameter `sections` bestimmt die Sektionen der Agentenausgabe, die das Check-Plugin abonniert. -Mit `myhostgroups` wird hier festgelegt, dass das neue Check-Plugin die gleichen Daten nutzt wie das alte: die durch die Parse-Funktion aufbereitete Sektion des Agentenplugins. -Der Service-Name enthält jetzt den Platzhalter `%s`. -An dieser Stelle wird später dann von {CMK} der Name des Items eingesetzt. -In den letzten beiden Zeilen werden schließlich die Namen für die neue Discovery-Funktion und die neue Check-Funktion festgelegt, die beide noch geschrieben werden wollen. - -Zuerst zur Discovery-Funktion, die jetzt die Aufgabe hat, die zu überwachenden Items zu ermitteln: - -[{python}] ----- -def discover_myhostgroups_advanced(section): - for group in section: - if group != "check_mk": - yield Service(item=group) ----- - -Wie xref:discovery_function[zuvor] bekommt die Discovery-Funktion das Argument `section`. -Mit einer Schleife werden die einzelnen Host-Gruppen durchlaufen. -Uns interessieren alle Host-Gruppen -- mit Ausnahme von `check_mk`, denn um diese spezielle Host-Gruppe kümmert sich bereits das existierende Check-Plugin `myhostgroups`. -Immer, wenn wir ein Item gefunden haben, geben wir das mit `yield` zurück, wobei ein Objekt vom Typ `Service` erzeugt wird, das den Host-Gruppennamen als Item bekommt. - - -Wenn später der Host überwacht wird, dann wird die Check-Funktion für jeden Service -- und damit für jedes Item -- separat aufgerufen. -Womit wir bereits bei der Definition der Check-Funktion für das neue Check-Plugin `myhostgroups_advanced` angekommen sind. -Die Check-Funktion bekommt zusätzlich zur Sektion das Argument `item`. -Die erste Zeile der Funktion sieht dann so aus: - -[{python}] ----- -def check_myhostgroups_advanced(item, section): ----- - -Der Algorithmus für die Check-Funktion ist einfach: -Wenn die Host-Gruppe existiert, wird der Service auf {OK} gesetzt und alle Hosts der Host-Gruppe aufgelistet. -Die komplette Funktion dazu: - -[{python}] ----- -def check_myhostgroups_advanced(item, section): - attr = section.get(item) - if attr: - yield Result(state=State.OK, summary=f"Hosts in this group: {attr['members']}") ----- - -Das Check-Ergebnis wird geliefert, indem wir ein Objekt vom Typ `Result` per `yield` zurückgeben. -Dieses benötigt die Parameter `state` und `summary`. -`state` legt den Zustand des Services fest (im Beispiel `OK`) und `summary` den Text, der in der [.guihint]#Summary# des Services angezeigt wird. -Er ist rein informell und wird von {CMK} nicht weiter ausgewertet. -Mehr dazu erfahren Sie in xref:summary_details[einem] der nächsten Abschnitte. - -So weit, so gut. -Was passiert aber, wenn das gesuchte Item nicht gefunden wird? -Das kann passieren, wenn in der Vergangenheit ein Service für eine Host-Gruppe bereits erzeugt wurde, diese Host-Gruppe aber nun verschwunden ist --- entweder weil die Host-Gruppe in {CMK} noch existiert, aber keinen Host mehr enthält, oder weil sie gleich ganz gelöscht wurde. -In beiden Fällen ist diese Host-Gruppe in der Agentenausgabe nicht (mehr) präsent. - -Die gute Nachricht: -{CMK} kümmert sich darum! -Wird ein gesuchtes Item nicht gefunden, so erzeugt {CMK} _automatisch_ für den Service das Resultat `UNKNOWN - Item not found in monitoring data`. -Das ist so gewollt und gut so. -Wenn ein gesuchtes Item nicht gefunden wird, so können Sie Python einfach aus der Funktion herauslaufen und {CMK} seine Arbeit erledigen lassen. - -{CMK} weiß nur, dass das Item, das vorher da war, nun weg ist. -Den Grund dafür kennt {CMK} nicht -- aber wir. -Darum ist es legitim, unser Wissen nicht für uns zu behalten und diesen Fall in der Check-Funktion abzufangen und dabei eine hilfreiche Meldung ausgeben zu lassen. - -[{python},highlight=3-5] ----- -def check_myhostgroups_advanced(item, section): - attr = section.get(item) - if not attr: - yield Result(state=State.CRIT, summary="Group is empty or has been deleted") - return - yield Result(state=State.OK, summary=f"Hosts in this group: {attr['members']}") ----- - -// TK: Blöderweise muss ich hier die if-then-else Logik umstellen, damit die Funktion für die folgenden Kapitel vorbereitet wird. -Was hat sich geändert? -Der Fehlerfall wird jetzt zuerst abgehandelt. -Daher überprüfen wir im `if`-Zweig, ob das Item _nicht_ existiert, setzen den Status auf {CRIT} und verlassen mit `return` die Funktion. -In allen anderen Fällen geben wir, wie zuvor, {OK} zurück. - -Damit haben wir in der Check-Funktion den Fall der verschwundenen Host-Gruppen übernommen. -Statt {UNKNOWN} wird der zugehörige Service nun {CRIT} sein mit der Information über die Ursache des kritischen Zustands. - -Damit ist das neue Check-Plugin als Erweiterung des alten fertiggestellt. -Auch die erweiterte Datei finden Sie wieder auf -link:https://github.com/Checkmk/checkmk-docs/blob/master/examples/devel_check_plugins/check_plugin_advanced_myhostgroups.py[GitHub.^] -Sie enthält das einfache Check-Plugin `myhostgroups` aus dem xref:first_check_plugin[vorherigen Kapitel], die erweiterte Parse-Funktion und die Komponenten des neuen Check-Plugins `myhostgroups_advanced` mit der Registrierung, der Discovery-Funktion und der Check-Funktion. -Beachten Sie, dass die Funktionen immer vor dem Registrieren definiert werden müssen, damit es keine Fehler wegen nicht definierter Funktionsnamen gibt. - -// TK: Nicht verwendeter Inhalt aus: -// TK: 3. Checks mit mehr als einem Service pro Host (Items) -// TK: Single vs. Multi-Items (Prio 1) -//// - -Die Discovery-Funktion hat jetzt die Aufgabe, die zu überwachenden Items zu ermitteln. -Wie gehabt bekommt sie das Argument `section`. -Und auch hier handelt es sich um eine Liste von Zeilen, welche ihrerseits wiederum Listen von Worten sind. -Diese sieht in unserem Beispiel aus aus: - -[{python}] ----- -[['West', '100', '100'], ['East', '197', '200'], ['North', '0', '50']] ----- - -So eine Liste kann man mit Python prima in einer Schleife durchlaufen und den drei Worten pro Zeile gleich sinnvolle Namen geben: - -[{python}] ----- -for sector, _used, _slots in section: - ... ----- - -... - -Der Unterstrich zeigt an, dass uns die beiden andere Spalten in der Ausgabe erst einmal egal sind, -denn bei der Discovery ist es schließlich unerheblich, wie viele Slots belegt sind. -Insgesamt sieht das dann so aus: - -[{python}] ----- -def discover_foobar(section): - for sector, _used, _slots in section: - yield Service(item=sector) ----- - -Es wäre natürlich ein Leichtes, hier anhand von beliebigen Kriterien manche Zeilen auszulassen. -Vielleicht gibt es ja Sektoren, welche die Größe 0 haben und die man grundsätzlich nie überwachen möchte? -Lassen Sie solche Zeilen einfach aus und „yielden“ Sie dafür kein Item. - -... - -Jetzt gehen wir wieder alle Zeilen der Reihe nach durch. Dabei suchen wir diejenige Zeile heraus, die zum gewünschten Item gehört: - -[{python}] ----- -def check_foobar(item, section): - for sector, used, slots in section: - if sector == item: - ... ----- - -Jetzt fehlt nur noch die eigentliche Logik, welche festlegt, wann das Ding denn überhaupt {OK}, {WARN} oder {CRIT} sein soll. -Wir machen es hier so: - -* Wenn alle Slots belegt sind, soll das Ding {CRIT} werden. -* Wenn weniger als 10 Slots frei sind, dann wird es {WARN}. -* Ansonsten {OK} - -Die belegten und die verfügbaren Slots kommen ja immer als Wort zwei und drei in jeder Zeile vor. -Aber: es handelt sich hier um Strings, nicht um Zahlen. -Diese brauchen wir aber, um vergleichen und rechnen zu können. -Daher wandeln wir die Strings mit `int()` in Zahlen um. - -Das Check-Ergebnis liefern wir dann, indem wir ein Objekt vom Typ `Result` per `yield` liefern. -Dieses benötigt die Parameter `state` und `summary`: - -[{python}] ----- -def check_foobar(item, section): - for sector, used, slots in section: - if sector == item: - used = int(used) # convert string to int - slots = int(slots) # convert string to int - if used == slots: - s = State.CRIT - elif slots - used <= 10: - s = State.WARN - else: - s = State.OK - yield Result( - state = s, - summary = f"Used {used} out of {slots} slots") - return ----- - -Dazu noch folgende Hinweise: - -. Der Befehl `return` sorgt dafür, dass die Check-Funktion nach dem Bearbeiten des gefundenen Items sofort abgebrochen wird. Es gibt schließlich auch nichts mehr weiter zu tun. - -Übrigens gibt es für den häufigen Fall, dass Sie eine einfache Metrik auf Schwellwerte prüfen wollen, die Hilfsfunktion `check_levels`. -Diese Hilfsfunktion wird in der Check-API-Dokumentation erläutert, die Sie in {CMK} über [.guihint]#Help > Check plugin API reference > Agent based API ("Check API")# aufrufen können. - -// COMMENT[Die folgenden Ausgaben sind gefaket. Hier muss im CMK noch die Ausgabe gefixt werden.] - -Probieren wir jetzt zunächst die Discovery aus. -Der Übersicht halber beschränken wir das ganze mit der Option `--detect-plugins=foobar` auf unser Plugin: - -[{shell}] ----- -{c-omd} cmk --detect-plugins=foobar -vI myhost123 - [green]#3# foobar -SUCCESS - Found 3 services, 1 host labels ----- - -Und jetzt können wir auch gleich das Checken ausprobieren (ebenfalls auf `foobar` begrenzt): - -[{shell}] ----- -{c-omd} cmk --detect-plugins=foobar -v myhost123 -Foobar Sector East [yellow]#WARN - used 197 out of 200 slots# -Foobar Sector North [green]#OK - used 0 out of 50 slots# -Foobar Sector West [red]#CRIT - used 100 out of 100 slots# ----- -//// - - -[#summary_details] -=== Summary und Details - -Im Monitoring von {CMK} hat jeder Service neben dem Status -- {OK}, {WARN}, usw. -- auch eine Zeile Text. -Diese steht in der Spalte [.guihint]#Summary,# hat also die Aufgabe einer knappen Zusammenfassung des Zustands. -Die Idee ist, dass dieser Text eine Länge von 60 Zeichen nicht überschreitet. -Das sorgt dann immer für eine übersichtliche Tabellendarstellung ohne störende Zeilenumbrüche. - -.Die Summary wird im Monitoring auf der Seite [.guihint]#Services of Host# angezeigt -image::devel_cpi_service_summary.png[alt="Die Summary in der Service-Übersicht."] - -Daneben gibt es noch das Feld [.guihint]#Details,# in dem alle Details zum Zustand des Services angezeigt werden, wobei alle Informationen des Summary auch in den Details enthalten sind. -Nach Anklicken des Services wird die Service-Seite geöffnet, in der neben vielen anderen auch die beiden Felder [.guihint]#Summary# und [.guihint]#Details# zu sehen sind. - -Beim Aufruf von `yield Result(...)` können Sie bestimmen, welche Informationen so wichtig sind, dass sie in der Summary angezeigt werden sollen, und bei welchen es genügt, dass diese in den Details erscheinen. - -In unserem Beispiel haben wir bisher immer einen Aufruf der folgenden Art verwendet: - -[{python}] ----- - yield Result( - state=State.OK, - summary=f"Hosts in this group: {attr['members']}", - ) ----- - -Dieser führt dazu, dass `Hosts in this group:` gefolgt von der Liste der in der Host-Gruppe enthaltenen Hosts immer in der [.guihint]#Summary# erscheint -- und zusätzlich auch in den [.guihint]#Details#. -Dies sollten sie also nur für wichtige Informationen verwenden. -Enthält eine Host-Gruppe viele Hosts, kann die Liste sehr lang werden -- länger als die empfohlene Länge von 60 Zeichen. -Ist eine Information eher untergeordnet, so können Sie mit `details` festlegen, dass der Text _nur_ in den Details erscheint: - -[{python}] ----- - yield Result( - state=State.OK, - summary=f"{attr['num_hosts']} hosts in this group", - details=f"{attr['num_hosts']} hosts in this group: {attr['members']}", - ) ----- - -Im obigen Beispiel wird die Liste der Hosts daher nur noch in den [.guihint]#Details# angezeigt. -In der [.guihint]#Summary# steht dann nur die Anzahl der Hosts in der Gruppe: - -.Unterschiedliche Inhalte für Summary und Details im Monitoring -image::devel_cpi_service_summary_details.png[alt="Die Summary und Details in den Service-Details."] - -Es gibt neben `summary` und `details` noch einen dritten Parameter. -Mit `notice` bestimmen Sie, dass ein Text für einen Service im Zustand {OK} _nur_ in den Details angezeigt wird -- aber zusätzlich in der Summary für alle anderen Zustände. -Somit wird dann aus der Summary sofort klar, warum der Service nicht {OK} ist. -Der Parameter `notice` ist nicht besonders sinnvoll, wenn Texte fest an Zustände gebunden sind, wie bisher in unserem Beispiel. -Das ändert sich im nächsten Kapitel, in dem es um mehrere Teilresultate pro Service geht. - -Zusammengefasst bedeutet das: - -* Der Gesamttext für die Summary sollte bei Services, die {OK} sind, nicht länger als 60 Zeichen sein. -* Verwenden Sie immer entweder `summary` oder `notice` -- nicht beides und nicht keines davon. -* Fügen Sie bei Bedarf `details` hinzu, wenn der Text für die Details ein alternativer sein soll. - -// TK: Nicht verwendeter Inhalt aus: -// TK: 6. Summary und Details -//// -Dabei gilt die Regel, dass Teilergebnisse, die zu einem {WARN}/{CRIT} führen, _immer_ in der Summary sichtbar werden. - -... - -Ist ein Teilergebnis eher untergeordnet, so ersetzen Sie `summary` durch `notice` und der Text erscheint, falls der Service {OK} ist, _nur_ in den Details. - -[{python}] ----- - yield Result(state=State.OK, notice="some additional text") ----- - -Falls der Zustand {WARN} oder {CRIT} ist, taucht der Text dann automatisch zusätzlich in der Summary auf: - -[{python}] ----- - yield Result(state=State.CRIT, notice="some additional text") ----- - -Zu guter Letzt haben Sie noch -- sowohl bei `summary` als auch bei `notice` die Möglichkeit, für die Details einen _alternativen_ Text anzugeben, der eventuell mehr Informationen zu dem Teilergebnis enthält: - -[{python}] ----- - yield Result(state=State.OK, - summary="55% used space", - details="55.2% of 160 GB used (82 GB)") ----- -//// - - -[#partial_results] -=== Mehrere Teilresultate pro Service - -Um die Anzahl der Services auf einem Host nicht ins Unermessliche steigen zu lassen, sind in einem Service oft mehrere Teilresultate zusammengefasst. -So prüft z.B. der Service [.guihint]#Memory# unter Linux nicht nur RAM- und Swap-Nutzung, sondern auch geteilten Speicher (_shared memory_), Page-Tabellen und alles Mögliche andere. - -Die xref:check_api_doc[Check-API] bietet dafür eine sehr komfortable Schnittstelle. -So darf eine Check-Funktion einfach beliebig oft ein Ergebnis mit `yield` erzeugen. -Der Gesamtstatus des Services richtet sich dann nach dem schlechtesten Teilergebnis in der Reihenfolge {OK} → {WARN} → {UNKNOWN} → {CRIT}. - -Wir werden diese Möglichkeit in unserem Beispiel nutzen und für jeden Service der Host-Gruppen zu dem bestehenden zwei weitere Teilresultate definieren. -Diese werten den Prozentsatz der Hosts im Zustand {UP} und der Services im Zustand {OK} aus. -Dabei nutzen wir die xref:prepare[zuvor] in der Agentenausgabe und der Parse-Funktion festgelegten zusätzlichen Spalten der Host-Gruppentabelle. - -Für den Vergleich eines ermittelten Werts mit Schwellwerten stellt die Check-API die Hilfsfunktion `check_levels` bereit. -Damit diese Funktion genutzt werden kann, müssen wir sie importieren und daher am Anfang der Datei das import-Kommando um `check_levels` erweitern: - -[{python}] ----- -from .agent_based_api.v1 import check_levels, register, Result, Service, State ----- - -Nachdem diese Voraussetzung abgehakt ist, erweitern wir die Check-Funktion: - -[{python}] ----- -def check_myhostgroups_advanced(item, section): - attr = section.get(item) - if not attr: - yield Result(state=State.CRIT, summary="Group is empty or has been deleted") - return - - members = attr["members"] - num_hosts = int(attr["num_hosts"]) - num_hosts_up = int(attr["num_hosts_up"]) - num_services = int(attr["num_services"]) - num_services_ok = int(attr["num_services_ok"]) ----- - -Der `if`-Zweig bleibt unverändert, d.h. die neuen Teilresultate gelten nur für Host-Gruppen, die auch existieren. -Anschließend definieren wir fünf Variablen für die in der Sektion enthaltenen Spalten. -Dies erhöht zum einen im folgenden die Lesbarkeit und nebenbei können wir für die vier Spalten, mit denen noch gerechnet werden soll, die ausgelesenen Strings mit `int()` in Zahlen umwandeln. - -Auch das bisher einzig existierende Resultat bleibt (fast) unverändert: - -[{python}] ----- - yield Result( - state=State.OK, - summary=f"{num_hosts} hosts in this group", - details=f"{num_hosts} hosts in this group: {members}" - ) ----- - -Nur der Zugriff im Python „F-String“ auf den Ausdruck, der den Wert liefert, ist nun einfacher als zuvor, da das `attr` bereits in den Variablendefinitionen steckt. - -Nun zur eigentlichen Erweiterung, d.h. der Definition eines Resultats, das die folgende Aussage umsetzt: -„Der Service der Host-Gruppe ist {WARN}, wenn 90% der Hosts {UP} sind, und {CRIT}, wenn 80% der Hosts {UP} sind.“ -Das sieht dann so aus: - -[{python}] ----- - hosts_up_perc = num_hosts_up / num_hosts * 100 - yield from check_levels(hosts_up_perc, levels_lower=(90, 80), label="UP hosts", notice_only=True) ----- - -In der ersten Zeile wird aus Gesamtzahl und Zahl der Hosts im Zustand {UP} der Prozentsatz berechnet und in der Variablen `hosts_up_perc` gespeichert. -In der zweiten Zeile wird dann das Ergebnis der Funktion `check_levels` zurückgegeben. -Die Funktion wird gefüttert mit dem gerade berechneten Prozentsatz als Wert (`hosts_up_perc`), den beiden unteren Schwellwerten (`levels_lower`), einer Bezeichnung, die der Ausgabe vorangestellt wird (`label`) und schließlich mit `notice_only=True`. - - -// TK: alter Inhalt aus: 5. Checks mit mehreren Teilresultaten - -//// -Die belegten und die verfügbaren Slots kommen ja immer als Wort zwei und drei in jeder Zeile vor. -Aber: es handelt sich hier um Strings, nicht um Zahlen. -Diese brauchen wir aber, um vergleichen und rechnen zu können. -Daher wandeln wir die Strings mit `int()` in Zahlen um. - -Übrigens gibt es für den häufigen Fall, dass Sie eine einfache Metrik auf Schwellwerte prüfen wollen, die Hilfsfunktion `check_levels`. -Diese Hilfsfunktion wird in der Check-API-Dokumentation erläutert, die Sie in {CMK} über [.guihint]#Help > Check plugin API reference > Agent based API ("Check API")# aufrufen können. -//// - -//// -== Checks mit mehreren Teilresultaten - -Um die Anzahl der Services auf einem Host nicht ins Unermessliche steigen zu -lassen, sind in einem Service oft mehrere Teilresultate zusammengefasst. So -prüft z.B. der Service [.guihint]#Memory used# unter Linux nicht nur den RAM- und Swap-Nutzung, -sondern auch Shared memory, Page-Tabellen und alles mögliche Andere. - -Die API von {CMK} bietet dafür eine sehr komfortable Schnittstelle. So darf eine -Check-Funktion einfach beliebig oft ein Ergebnis mit `yield` erzeugen. Der -Gesamtstatus des Services richtet sich dann nach dem „schlechtesten“ Teilergebnis -nach dem Schema {OK} → {WARN} → {UNKNOWN} → {CRIT}. - -Hier ist ein gekürztes fingiertes Beispiel: - -[{python}] ----- -def check_foobar(section): - yield Result(state=State.OK, summary="Knulf rate optimal") - # ... - yield Result(state=State.WARN, summary="Gnarz required") - # ... - yield Result(state=State.OK, summary="Last Szork was good") ----- - -Die Summary des Services in der GUI sieht dann so aus: -„Knulf rate optimal, Gnarz required {WARN}, Last Szork was good“. -Und der Gesamtstatus ist {WARN}. - -Auf die gleiche Art können Sie auch mehrere Metriken zurückgeben. -Rufen Sie einfach für jede Metrik einmal `yield Metric(...)` -auf. -//// - - - - -=== Metriken -// TK: alter Inhalt aus: 4. Messwerte - -//// -== Messwerte - -=== Werte in der Check-Funktion ermitteln - -Nicht immer, aber oft befassen sich Checks mit Zahlen. Mit seinem xref:graphing#[Graphingsystem] -hat {CMK} eine Komponente, um solche Zahlen zu speichern, auszuwerten und darzustellen. Das -geht dabei völlig unabhängig von der Berechnung der Zuständige {OK}, {WARN} und {CRIT}. - -Solche Messwerte -- oder auch Metriken genannt -- werden von der Check-Funktion ermittelt -und einfach als zusätzliches Ergebnis zurückgegeben. Dazu dient das Objekt `Metric`, -welches mindestens die beiden Argumente `name` und `value` benötigt. -Hier ist ein Beispiel: - -[{python}] ----- - yield Metric("fooslots", used) ----- - - -[#thresholdinformation] -=== Informationen zu den Schwellwerten - -Weiterhin gibt es noch zwei optionale Argumente. Mit dem Argument `levels` können Sie eine Information -zu Schwellwerten für {WARN} und {CRIT} mitgeben, und zwar in Form eines Paares von zwei Zahlen. -Diese wird dann üblicherweise im Graphen als gelbe und rote Linie eingezeichnet. Die erste Zahl -steht für die Warnschwelle, die zweite für die kritische. -Dabei gilt die Konvention, dass der Check beim Erreichen der Warnschwelle -bereits auf {WARN} geht (bei {CRIT} analog). - -Das sieht dann z.B. so aus (hier mit hartkodierten Schwellwerten): - -[{python}] ----- - yield Metric("fooslots", used, levels=(190,200)) ----- - -Hinweise: - -* Falls nur eine der beiden Schwellen definiert ist, tragen Sie für die andere einfach `None` ein, also z.B. `levels=(None, 200)`. -* Es sind auch Fließkommazahlen erlaubt, aber keine Strings. -* Achtung: für die _Überprüfung_ der Schwellwerte ist die Check-Funktion selbst verantwortlich. Die Angabe von `levels` dient lediglich als Randinformation für das Graphingsystem! - - -=== Der Wertebereich - -Analog zu den Schwellwerten können Sie dem Graphingsystem auch die Information über -den möglichen Wertebereich mitgeben. Damit ist der kleinste und größte mögliche Wert -gemeint. Das geschieht im Argument `boundaries`, wobei auch hier optional -für eine der beiden Grenzen `None` eingesetzt werden kann. Beispiel: - -[{python}] ----- - yield Metric(name="fooslots", value=used, boundaries=(0, 200)) ----- - -Und jetzt unsere Check-Funktion aus dem obigen Beispiel nochmal, aber -diesmal mit der Rückgabe von Metrikinformation inklusive Schwellwerte und -Wertebereich (diesmal natürlich nicht mit fixen sondern mit berechneten Werten): - -[{python}] ----- -def check_foobar(item, section): - for sector, used, slots in section: - if sector == item: - used = int(used) # convert string to int - slots = int(slots) # convert string to int - - yield Metric( - "fooslots", - used, - levels=(slots-10, slots), - boundaries=(0, slots)) - - if used == slots: - s = State.CRIT - elif slots - used <= 10: - s = State.WARN - else: - s = State.OK - yield Result( - state = s, - summary = f"used {used} out of {slots} slots") - return ----- - -// COMMENT[Irgendwo müssen wir noch schreiben, dass man cmk -R braucht, damit der Check auch im laufenden CMK funktioniert. Bzw. überhaupt zeigen, wie das mit der Kommandozeile und dem System interagiert. Das cmk -R braucht man vor allem, um dann die metriken auszuprobieren.] -// COMMENT[Die Option -p wäre noch nützlich zu zeigen hier] -// COMMENT[Hinweise darauf, dass die Definition von Graphen etc. später kommt und dass man den Defaultgraph bekommt] -// COMMENT[Hinweis, welchen Namen man für seine Metrik nehmen soll!] -//// - - - - - -=== Check-Parameter für Regelsätze -// TK: alter Inhalt aus: 10. Schwellwerte und Check-Parameter - -//// -[#parameters] -== Schwellwerte und Check-Parameter - -=== Ein Regelsatz für das Setup - -In einem unserer bisherigen Beispiele haben wir den Zustand {WARN} -erzeugt, falls nur noch 10 oder weniger Slots frei waren. Dabei war die -Zahl `10` direkt in der Check-Funktion fest einprogrammiert - hart codiert, -wie Programmierer sagen würden. In {CMK} ist man allerdings als Anwender -eher gewohnt, dass man solche Schwellwerte und Parameter per _Regel_ -konfigurieren kann. Deswegen wollen wir uns als nächstes ansehen, wie -auch Sie Ihren Check so verbessern können, dass er über die Setup-Oberfläche -konfigurierbar ist. - -Dazu müssen wir zwei Fälle unterscheiden: - -. Es gibt bereits einen passenden Regelsatz. Das kann eigentlich nur dann der Fall sein, wenn Ihr neuer Check etwas prüft, für das {CMK} in gleicher Form bereits Check-Plugins hat, z.B. das Überwachen einer Temperatur. Dafür gibt es bereits einen Regelsatz, den Sie direkt verwenden können. -. Es gibt keinen passenden Regelsatz. Dann müssen Sie einen neuen anlegen. - - -=== Verwenden von vorhandenen Regelsätzen - -Die ausgelieferten Regelsätze für Parameter von Checks finden Sie -im Verzeichnis `lib/check_mk/gui/plugins/wato/check_parameters/`. -Nehmen wir als Beispiel die Datei `memory_simple.py`. Diese -deklariert einen Regelsatz mit folgendem Abschnitt: - -.~/lib/check_mk/gui/plugins/wato/check_parameters/memory_simple.py -[{python}] ----- -rulespec_registry.register( - CheckParameterRulespecWithItem( - check_group_name="memory_simple", - group=RulespecGroupCheckParametersOperatingSystem, - item_spec=_item_spec_memory_simple, - match_type="dict", - parameter_valuespec=_parameter_valuespec_memory_simple, - title=lambda: _("Main memory usage of simple devices"), - )) ----- - -Entscheidend für Sie ist dabei das Schlüsselwort `check_group_name`, -welches hier auf `"memory_simple"` gesetzt ist. Damit wird die Verbindung -zum Check-Plugin hergestellt. Das machen Sie beim Registrieren des Checks -mit dem Schlüsselwort `check_ruleset_name`, zum Beispiel: - -[{python}] ----- -register.check_plugin( - name = "foobar", - service_name = "Foobar Sector %s", - discovery_function = discover_foobar, - check_function = check_foobar, - check_ruleset_name="memory_simple", - check_default_parameters={}, -) ----- - -Zwingend notwendig ist dabei auch die Definition von Standardparametern -über das Schlüsselwort `check_default_parameters`. Diese Parameter -gelten für Ihren Check dann, wenn der Benutzer noch keine Regel angelegt -hat. Falls es keine verpflichtenden Parameter gibt, können Sie einfach -das leere Dictionary `{}` als Wert nehmen. - -Wie der jeweils vom Benutzer konfigurierte Wert dann bei der Check-Funktion -ankommt, werden wir dann weiter unten sehen. - - -=== Einen eigenen Regelsatz definieren - -Falls es keinen passenden Regelsatz gibt (was wohl eher der Normalfall -ist), müssen wir uns selbst einen neuen erzeugen. Dazu legen wir -eine Datei im Verzeichnis `~/local/share/check_mk/web/plugins/wato` -an. Der Name der Datei sollte sich an dem des Checks orientieren und -er muss wie alle Plugin-Dateien die Endung `.py` haben. - -// wichtig: nur Konstanten - z.b. keine f-Strings verwenden! -Sehen wir uns den Aufbau so einer Datei Schritt für Schritt an. -Zunächst kommen einige Importbefehle. Falls die Texte in Ihrer -Datei in andere Sprachen übersetzbar sein sollen, importieren -Sie `+_+` (Unterstrich). Dies ist eine Funktion und fungiert als Markierung -für alle übersetzbaren -Texte. Im Weiteren schreiben Sie dann z.B. anstelle -von `"Threshold for warn"` ein `+_("Threshold for warn")+` für den Funktionsaufruf. - -Das Übersetzungssystem von {CMK}, welches auf -link:https://docs.python.org/3/library/gettext.html#module-gettext[gettext^] -basiert, findet -solche Texte und übernimmt sie in die Liste der zu übersetzenden Texte -auf. Falls Sie den Check nur für sich selbst bauen, können Sie darauf -auch verzichten und brauchen den folgenden Importbefehl nicht: - -.~/local/share/check_mk/web/plugins/wato/foobar_parameters.py -[{python}] ----- -from cmk.gui.i18n import _ ----- - -Als nächstes importieren wir sogenannte _ValueSpecs_. Ein ValueSpec -ist ein sehr praktisches und universelles Werkzeug, das {CMK} an vielen -Stellen verwendet. Es dient dem Generieren von angepassten Eingabemasken, -der Darstellung und Validierung der eingegebenen Werte und der Umwandlung -in Python-Datenstrukturen. In folgendem Beispiel werden `Dictionary`, -`Integer` und `TextInput` importiert. - -[{python}] ----- -from cmk.gui.valuespec import ( - Dictionary, - Integer, - TextInput, -) ----- - -Das `Dictionary` benötigen Sie auf jeden Fall. Denn seit Version -{v20} von {CMK} ist es zwingend vorgeschrieben, dass Check-Parameter -Python-Dictionaries sein müssen. Früher konnte es z.B. auch ein Paar (Tupel -aus zwei Zahlen) sein (z.B. Warn/Crit). - -`Integer` ist für die Eingabe einer Zahl ohne Kommastellen -verantwortlich und `TextInput` für einen Unicode-Text. - -Als nächstes werden noch Symbole importiert, die beim Registrieren -benötigt werden: - -[{python}] ----- -from cmk.gui.plugins.wato.utils import ( - CheckParameterRulespecWithItem, - rulespec_registry, - RulespecGroupCheckParametersOperatingSystem, -) ----- - -Falls Ihr Check kein Item hat, importieren Sie stattdessen -`CheckParameterRulespecWithoutItem`. Zur `RulespecGroup`.... -schreiben wir weiter unten noch etwas. - -Nun kommen die eigentlichen Definitionen. Zunächst deklarieren wir -ein Eingabefeld, mit dem der Benutzer das _Item_ des Checks angeben -kann. Dies ist für die Regelbedingung notwendig, und auch für das manuelle -Anlegen von Checks, welche ohne Discovery funktionieren sollen. Das erledigen -wir mit `TextInput`. Dieses bekommt per `title` einen Titel -zugewiesen, welcher dann in der Regel als Überschrift für das Eingabefeld -angezeigt wird: - -[{python}] ----- -def _item_valuespec_foobar(): - return TextInput(title="fitting item name", help="inline help text") ----- - -Den Namen der Funktion, welche diese ValueSpec zurückgibt, können -Sie frei wählen, er wird nur an der Stelle weiter unten benötigt. -Damit er nicht über die Modulgrenze hinaus sichtbar wird, sollte er -mit einem Unterstrich beginnen. - -Als nächstes kommt das ValueSpec für die Eingabe des eigentlichen -Check-Parameters. Auch hierfür legen wir eine Funktion an, welche -dieses erzeugt. Das `return Dictionary(...)` ist vorgeschrieben. -Innerhalb dessen legen Sie mit `elements=[...]` die Liste -der Unterparameter für diesen Check an. In unserem Beispiel gibt -es nur einen: die Warnschwelle für die freien Slots. Dies soll -eine Ganzzahl sein, also verwenden wir hier ein `Integer`. - -[{python}] ----- -def _parameter_valuespec_foobar(): - return Dictionary( - elements=[ - ("warning_lower", Integer(title=_("Warning below free slots"))), - ], - ) ----- - -Zu guter Letzt registrieren wir jetzt mithilfe der importierten und -selbstdefinierten Dinge einen neuen Regelsatz. Dazu gibt es die -Funktion `rulespec_registry.register()`: - -[{python}] ----- -rulespec_registry.register( - CheckParameterRulespecWithItem( - check_group_name="foobar", - group=RulespecGroupCheckParametersOperatingSystem, - match_type="dict", - item_spec=_item_valuespec_foobar, - parameter_valuespec=_parameter_valuespec_foobar, - title=lambda: _("Free slots for Foobar sectors"), - )) ----- - -Dazu noch einige Hinweise: - -* Falls Ihr Check kein Item verwendet, lautet die innere Funktion `CheckParameterRulespecWithoutItem`. Die Zeile `item_spec` entfällt dann. -* Wie oben erwähnt stellt der `check_group_name` die Verbindung zu den Checks her, welche diese Regel verwenden sollen. Er darf auf keinen Fall identisch sein mit einer bereits existierenden Regel, weil diese damit überschrieben würde. -* Die `group` legt fest, in welcher Kategorie im Setup der Regelsatz auftauchen soll. Die meisten dieser Gruppen sind in der Datei `~/lib/check_mk/gui/plugins/wato/utils/__init__.py` definiert. Dort finden Sie auch Beispiele, wie Sie eine eigene neue Gruppe anlegen können. -* Der `match_type` ist immer `"dict"`. In älteren {CMK}-Versionen gab es auch Parameterregeln mit anderen Typen. -* `title` legt den Titel des Regelsatzes fest, wird aber nicht direkt als Text, sondern als ausführbare Funktion angegeben, welche den Text zurückliefert (deswegen das `lambda:`). - - -==== Test - -Wenn Sie diese Datei angelegt haben, sollten Sie erstmal ausprobieren, ob alles soweit -funktioniert und nicht gleich mit der Check-Funktion weiterarbeiten. Dazu müssen -Sie erstmal den Apache der Instanz neu starten, damit die neue Datei gelesen wird. -Das macht der Befehl: - -[{shell}] ----- -{c-omd} omd restart apache ----- - -Danach sollte der Regelsatz im Setup zu finden sein. Legen Sie eine Regel -in dieser Kette an und probieren Sie verschiedene Werte aus. Wenn -das ohne Fehler geht, können Sie die Check-Parameter jetzt in der Check-Funktion -verwenden. - - -=== Die Regel im Check-Plugin benutzen - -Damit die Regel zum Greifen kommt, müssen wir dem Check-Plugin erlauben, -Check-Parameter entgegenzunehmen und ihm sagen, welche Regel benutzt -werden soll. Dazu muss bei der Registrierung der Eintrag `check_default_parameters` -unbedingt vorhanden sein. Im einfachsten Fall übergeben wir ein leeres Dictionary. - -Als zweites übergeben wir der Registrierungsfunktion noch den -`check_ruleset_name`, also den Namen, den wir oben mittels -`check_group_name` an den Regelsatz vergeben haben. So weiß {CMK} -aus welchem Regelsatz die Parameter bestimmt werden sollen. - -Das Ganze sieht dann z.B. so aus: - -[{python}] ----- -register.check_plugin( - name = "foobar", - service_name = "Foobar Sector %s", - discovery_function = discover_foobar, - check_function = check_foobar, - check_default_parameters={}, - check_ruleset_name="foobar", -) ----- - -Nun wird {CMK} versuchen, der Check-Funktion Parameter zu übergeben. Damit -das klappen kann, müssen wir die Check-Funktion so erweitern, dass sie als -zweites das Argument `params` erwartet. Diese schiebt sich zwischen -`item` und `section` (Falls Sie einen Check ohne Item bauen, -entfällt das `item` natürlich und `params` steht am Anfang): - -[{python}] ----- -def check_foobar(item, params, section): ----- - -Es ist sehr empfehlenswert, sich jetzt als ersten Test den -Inhalt der Variable `params` mit einem `print` -ausgeben zu lassen (oder `pprint`, wenn Sie es etwas -komfortabler haben wollen). Legen Sie verschiedene Regeln an, probieren -Sie, welche Werte bei `params` ankommen: - -[{python}] ----- -def check_foobar(item, params, section): - print(params) - for sector, used, slots in ... ----- - -Und ganz wichtig: Wenn alles fertig ist, entfernen Sie unbedingt die -`print`-Befehle wieder! Diese können die interne Kommunikation -von {CMK} durcheinanderbringen. - - -Nun passen wir unsere Check-Funktion an, so dass der übergebene Parameter -seine Wirkung entfalten kann. Wir holen uns den Wert mit dem in der Regel -gewählten Key (hier `"warning_lower"`) aus den Parametern: - -[{python}] ----- -def check_foobar(item, params, section): - warn = params["warning_lower"] - for sector, used, slots in section: - if sector == item: - used = int(used) # convert string to int - slots = int(slots) # convert string to int - if used == slots: - s = State.CRIT - elif slots - used <= warn: - s = State.WARN - else: - s = State.OK - yield Result( - state = s, - summary = f"used {used} out of {slots} slots") - return ----- - -Falls eine Regel konfiguriert ist, können wir nun die „freien Slots“ in -unserem Beispiel überwachen. Wenn allerdings keine Regel definiert ist, -wird diese Check-Funktion abstürzen: Da die Default-Parameter des Plugins nicht -befüllt sind, wird das Plugin bei Abwesenheit einer Regel einen `KeyError` -erzeugen. - -Dieses Problem können wir beheben, indem wir bei der Registrierung einen -passenden Parameter einfügen: - -[{python}] ----- -register.check_plugin( - name = "foobar", - service_name = "Foobar Sector %s", - discovery_function = discover_foobar, - check_function = check_foobar, - check_default_parameters = {"warning_lower": 10}, - check_ruleset_name = "foobar", -) ----- - -Sie sollten Defaultwerte immer auf diese Weise übergeben (und den Fall -fehlender Parameter nicht im Check-Plugin abfangen), da diese Standardparameter -auch in der Setup-Oberfläche angezeigt werden können. Dazu gibt es z.B. -auf der Servicekonfigurationsseite eines Hosts im Menu [.guihint]#Display# den Eintrag [.guihint]#Show Check parameters#. - -Ein einzelner Wert als Schwellwert ist in {CMK} übrigens sehr unüblich. Da -Services in den Zuständen {OK}, {WARN}, {CRIT} sein können, ist es -naheliegend die Parameter immer als `Tuple` mit zwei Einträgen zu -definieren, also als Paar von Schwellen für {WARN} und {CRIT}. Dazu passen -wir den Regelsatz wie folgt an: - -[{python}] ----- -def _parameter_valuespec_foobar(): - return Dictionary( - elements=[ - ("warning_lower", Tuple( - title=_("Levels on free slots"), - elements=[ - Integer(title=_("Warning below")), - Integer(title=_("Critical below")), - ], - )), - ], - ) ----- - -Beachten Sie, dass eine solche Änderung des Datentyps eine inkompatible -Änderung darstellt: Existierende Regeln können jetzt nicht mehr von der -Oberfläche geladen werden. Und auch die Check-Funktion kann auf Probleme -stoßen, wenn anstelle eines erwarteten Paar von zwei Zahlen eine einzelne -Zahl in `params` steht. Sie können solche Regeln einfach editieren. -Beim erneuten Speichern wird dann das neue Format verwendet. -//// - - -=== Imports -// TK: kein alter Inhalt - - - - -[#snmp] -== Ein Check-Plugin für SNMP schreiben -// TK: alter Inhalt aus: 8. SNMP-basierte Checks - -//// -[#snmp] -== SNMP-basierte Checks - -=== Grundsätzliches - -Das Entwickeln von Checks, die mit SNMP arbeiten, läuft sehr ähnlich zu den -agentenbasierten, nur dass Sie hier noch angeben müssen, welche SNMP-Bereiche -(OIDs) der Check benötigt. Falls Sie noch keine Erfahrung mit SNMP haben, -so empfehlen wir Ihnen an dieser Stelle als Vorbereitung unbedingt den -Artikel über das xref:snmp#[Monitoring via SNMP]. - -Der Ablauf der Discovery und des Checkens via SNMP ist etwas anders als beim -normalen Agenten. Denn anders also dort -- wo der Agent von sich aus alle -interessanten Informationen sendet -- müssen wir bei SNMP selbst genau -sagen, welche Datenbereiche wir benötigen. Ein Komplettabzug aller Daten -wäre zwar theoretisch möglich (via SNMP-Walk), dauert aber bei schnellen -Geräten eher im Bereich von Minuten und bei komplexen Switches gern auch -über eine Stunde. Daher scheidet das beim Checken und sogar auch bei der -Discovery aus. {CMK} geht deswegen etwas zielgerichteter vor. - -==== SNMP-Detection - -Die Serviceerkennung teilt sich in zwei Phasen auf. Zunächst geschieht die -_SNMP-Detection_. Diese ermittelt, welche Plugins -denn überhaupt auf dem jeweiligen Gerät interessant sind. Dazu werden einige -wenige SNMP-OIDs abgerufen -- und zwar einzelne, ohne Walk. Die wichtigste -davon ist die `sysDescr` (OID: `1.3.6.1.2.1.1.1.0`). Unter dieser -OID hält jedes SNMP-Gerät eine Beschreibung von sich selbst bereit, z.B. -`Cisco NX-OS(tm) n5000, Software (n5000-uk9),...`. - -Ausgehend von diesem Text kann man für sehr viele Plugins schon definitiv -entscheiden, ob diese hier Sinn ergeben. Wenn der Text noch nicht spezifisch -genug ist, werden weitere OIDs geholt und geprüft. Ergebnis der SNMP-Detection -ist dann eine Kandidaten-Liste von Check-Plugins. - -==== Discovery - -Im zweiten Schritt werden für jeden dieser Kandidaten die jeweils nötigen -Monitoring-Daten mit SNMP-Walks geholt. Diese werden dann zu einer Tabelle -zusammengefasst und der Discovery-Funktion des Checks in dem Argument -`section` bereitgestellt, welche dann daraus wie gewohnt die zu -überwachenden Items ermittelt. - -==== Checken - -Beim Checken ist ja schon bekannt, welche Plugins für das Gerät ausgeführt -werden sollen und die SNMP-Detection entfällt. Hier werden gleich per -SNMP-Walks die für die Plugins benötigten Monitoring-Daten geholt und daraus -das Argument `section` für die Check-Funktion befüllt. - -==== Zusammenfassung - -Was müssen Sie also bei einem SNMP-Check anders machen als bei einem agentenbasierten? - -. Sie benötigen kein Plugin für den Agenten. -. Sie müssen die für die SNMP-Detection nötigen Einzel-OIDs und Suchtexte festlegen. -. Sie müssen festlegen, welche SNMP-Bereiche für das Monitoring geholt werden müssen. - -=== Ein Wort zu den MIBs - -Bevor wir weitermachen wollen wir hier noch ein Wort zu den berüchtigten -SNMP-MIBs verlieren, denn über diese gibt es viele Vorurteile. Gleich zu -Beginn eine gute Nachricht: {CMK} benötigt sie nicht. Wirklich! Sie sind -aber eine wichtige Hilfe, um einen SNMP-Check _entwickeln_ zu können. - -Was ist nun eine MIB? Wörtlich bedeutet die Abkürzung _Management -Information Base_ -- etwas nichtssagend. Konkret ist eine MIB eine -ganz gut lesbare Textdatei, welche einen bestimmten Teilbaum der SNMP-Welt -beschreibt. Und zwar steht hier, welcher Ast im Baum -- also welche _OID_ -- -welche Bedeutung hat. Das umfasst einen Namen für die OID, einen Hinweis, -welche Werte diese annehmen kann (z.B. bei enumerierten Datentypen, wo -dann Dinge wie 1=up, 2=down, etc. festgelegt sind) und manchmal auch noch -einen nützlichen Kommentar. - -{CMK} liefert eine Reihe von frei verfügbaren MIB-Dateien mit aus. Diese -beschreiben sehr allgemeine Bereiche im globalen OID-Baum, enthalten aber -keine herstellerspezifischen Bereiche. Daher helfen sie für selbst entwickelte -Checks nicht viel weiter. - -Versuchen Sie also, die für Ihr spezielle Gerät relevanten MIB-Dateien irgendwo -auf den Webseiten vom Hersteller oder sogar auf dem Management-Interface -des Geräts zu finden und installieren Sie diese in der {CMK}-Instanz nach -`local/share/check_mk/mibs`. Dann können Sie in SNMP-Walks OID-Nummern -in Namen umrechnen lassen und so schneller finden, wo die für das Monitoring -interessanten Daten sind. Wie gesagt, enthalten die MIBs außerdem interessante Informationen in den Kommentaren -- wenn sie sorgfältig gemacht sind. -Sie können eine MIB-Datei einfach mit einem Texteditor oder mit `less` ansehen. - - -[#locating_oids] -=== Die richtigen OIDs finden - -Die entscheidende Voraussetzung, um ein Plugin zu entwickeln, ist natürlich, -dass Sie wissen, welche OIDs die notwendigen Informationen enthalten. Der -erste Schritt dabei ist (falls das Gerät das nicht verweigert), einen kompletten -SNMP-Walk zu ziehen. Dabei werden _alle_ per SNMP verfügbaren Daten -abgerufen. - -{CMK} kann das sehr einfach für Sie erledigen. Nehmen Sie dazu zunächst -das Gerät (oder eines der Geräte), für das Sie ein Plugin entwickeln wollen, -ins Monitoring auf. Sagen wir es heißt `mydevice01`. Stellen Sie sicher, -dass dieses in den Grundfunktionen überwacht werden kann. Zumindest müssen -die Services [.guihint]#SNMP Info# und [.guihint]#Uptime# gefunden werden und wahrscheinlich -auch noch mindestens ein [.guihint]#Interface#. So stellen Sie sicher, dass der SNMP-Zugriff -sauber funktioniert. - -Wechseln Sie dann auf die Kommandozeile der {CMK}-Instanz. Hier können -Sie mit folgendem Befehl einen kompletten Walk ziehen. Dabei empfehlen wir, -gleich die Option `-v` (verbose) zu verwenden: - -[{shell}] ----- -{c-omd} cmk -v --snmpwalk mydevice01 -mydevice01: -Walk on ".1.3.6.1.2.1"...3898 variables. -Walk on ".1.3.6.1.4.1"...6025 variables. -Wrote fetched data to /omd/sites/heute/var/check_mk/snmpwalks/mydevice01. ----- - -Wie bereits erwähnt, kann so ein Komplettwalk Minuten oder sogar -Stunden dauern (auch wenn letzteres eher selten ist). Werden Sie also -nicht nervös, wenn es hier etwas dauert. Der Walk wurde nun in der Datei -`var/check_mk/snmpwalks/mydevice01` gespeichert. Es handelt sich -dabei um eine gut lesbare Textdatei, die etwa so beginnt: - -.~/var/check_mk/snmpwalks/mydevice01 -[{file}] ----- -.1.3.6.1.2.1.1.1.0 JetStream 24-Port Gigabit L2 Managed Switch with 4 Combo SFP Slots -.1.3.6.1.2.1.1.2.0 .1.3.6.1.4.1.11863.1.1.3 -.1.3.6.1.2.1.1.3.0 546522419 -.1.3.6.1.2.1.1.4.0 hh@example.com -.1.3.6.1.2.1.1.5.0 sw-ks-01 -.1.3.6.1.2.1.1.6.0 Core Switch Serverraum klein -.1.3.6.1.2.1.1.7.0 3 -.1.3.6.1.2.1.2.1.0 27 ----- - -In jeder Zeile steht eine OID und danach deren Wert. Und gleich in der ersten Zeile finden Sie die -wichtigste, nämlich die `sysDescr`. - -Nun sind die OIDs nicht sehr aussagekräftig. Wenn die richtigen MIBs installiert sind, -können Sie diese in einem zweiten Schritt mit dem Befehl `cmk --snmptranslate` -in Namen umrechnen lassen. Am besten leiten Sie das Ergebnis, was ansonsten im Terminal -käme, in eine Datei um: - -[{shell}] ----- -OMD[heute]:~$ cmk --snmptranslate mydevice01 > translated -Processing 9923 lines. -finished. ----- - -Die Datei `translated` liest sich wie der ursprüngliche Walk, hat aber in jeder -Zeile nach dem `-->` einen übersetzten Wert für die OID: - -.translated -[{file}] ----- -.1.3.6.1.2.1.1.1.0 JetStream 24-Port Gigabit L2 Managed Switch with 4 Combo SFP Slots --> SNMPv2-MIB::sysDescr.0 -.1.3.6.1.2.1.1.2.0 .1.3.6.1.4.1.11863.1.1.3 --> SNMPv2-MIB::sysObjectID.0 -.1.3.6.1.2.1.1.3.0 546522419 --> DISMAN-EVENT-MIB::sysUpTimeInstance -.1.3.6.1.2.1.1.4.0 hh@example.com --> SNMPv2-MIB::sysContact.0 -.1.3.6.1.2.1.1.5.0 sw-ks-01 --> SNMPv2-MIB::sysName.0 -.1.3.6.1.2.1.1.6.0 Core Switch Serverraum klein --> SNMPv2-MIB::sysLocation.0 -.1.3.6.1.2.1.1.7.0 3 --> SNMPv2-MIB::sysServices.0 -.1.3.6.1.2.1.2.1.0 27 --> IF-MIB::ifNumber.0 -.1.3.6.1.2.1.2.2.1.1.1 1 --> IF-MIB::ifIndex.1 -.1.3.6.1.2.1.2.2.1.1.2 2 --> IF-MIB::ifIndex.2 ----- - -Beispiel: die OID `.1.3.6.1.2.1.1.4.0` hat den übersetzten Namen -`SNMPv2-MIB::sysContact.0`. Dies ist ein wichtiger Hinweis, der Rest -ist dann Übung, Erfahrung und natürlich experimentieren. - -=== Die Registrierung der SNMP-Sektion - -Wenn Sie also die notwendigen OIDs herausgefunden haben, geht es an die -eigentliche Entwicklung des Plugins. Das geschieht in drei Schritten: - -. Legen Sie für die SNMP-Detection fest, welche OIDs welche Texte enthalten müssen, damit Ihr Plugin ausgeführt werden soll. -. Deklarieren Sie, welche OID-Zweige für das Monitoring geholt werden müssen. -. Schreiben Sie ein Check-Plugin analog zu denjenigen für agentenbasierte Checks. - -Die ersten beiden Schritte erfolgen durch die Registrierung einer SNMP-Sektion. -Dies erledigen Sie durch den Aufruf von `register.snmp_section()`. Hier -geben Sie mindestens drei Argumente an: den Namen der Sektion (`name`), -die Angaben für die SNMP-Detection `detect` und die benötigten -OID-Zweige für das eigentlich Monitoring (`fetch`). Hier ist ein Beispiel für ein -fiktives Check-Plugin mit dem Namen `foo`: - -.~/local/lib/check_mk/base/plugins/agent_based/foo.py -[{python}] ----- -register.snmp_section( - name = "foo", - detect = startswith(".1.3.6.1.2.1.1.1.0", "foobar device"), - fetch = SNMPTree( - base = '.1.3.6.1.4.1.35424.1.2', - oids = [ - '4.0', - '5.0', - '8.0', - ], - ), -) ----- - -==== Die SNMP-Detection - -Mit dem Schlüsselwort `detect` geben Sie an, unter welchen Bedingungen -die Discovery-Funktion überhaupt ausgeführt werden soll. In unserem Beispiel -ist das der Fall, wenn der Wert der OID `.1.3.6.1.2.1.1.1.0` (also -die `sysDescr`) mit dem Text `foobar device` beginnt (wobei -Groß-/Kleinschreibung grundsätzlich nicht unterschieden wird). Neben -`startswith` gibt es noch eine ganze Reihe weiterer möglichen -Attribute. Dabei existiert von jedem auch eine negierte Form, welche mit -`not_` beginnt: - -[cols="28,33,~"] -|=== -|Attribut |Negation |Bedeutung - -|`equals(oid, needle)` |`not_equals(oid, needle)` |Der Wert der OID ist gleich dem Text `needle` -|`contains(oid, needle)` |`not_contains(oid, needle)` |Der Wert der OID enthält an irgendeiner Stelle den Text `needle` -|`startswith(oid, needle)` |`not_startswith(oid, needle)` |Der Wert der OID beginnt mit dem Text `needle` -|`endswith(oid, needle)` |`not_endswith(oid, needle)` |Der Wert der OID endet mit dem Text `needle` -|`matches(oid, regex)` |`not_matches(oid, regex)` |Der Wert der OID matcht auf den xref:regexes#[regulären Ausdruck] `regex`, und zwar hinten und vorne geankert, also mit einem exakten Match. Wenn Sie nur einen Teilstring benötigen, ergänzen Sie einfach vorne bzw. hinten noch ein `pass:[.*]` -|`exists(oid)` |`not_exists(oid)` |Erfüllt, wenn die OID auf dem Gerät verfügbar ist. Der Wert darf leer sein. -|=== - -Daneben gibt es noch die Möglichkeit, mehrere Tests mit `all_of` -oder `any_of` zu verknüpfen. `all_of` erfordert mehrere -erfolgreiche Attribute für eine positive Erkennung des Plugins. Folgendes -Beispiel findet auf einem Gerät das Plugin, wenn in der `sysDescr` der Text mit `foo` (oder `FOO` oder `Foo`) beginnt *und* -die OID `.1.3.6.1.2.1.1.2.0` den Text `.4.1.11863.` enthält: - -[{python}] ----- -detect = all_of( - startswith(".1.3.6.1.2.1.1.1.0", "foo"), - contains(".1.3.6.1.2.1.1.2.0", ".4.1.11863.") -) ----- - -`any_of` hingegen ist damit zufrieden, wenn auch nur eines der Kriterien -erfüllt ist. Hier ist ein Beispiel, in dem verschiedene Werte für die `sysDescr` -erlaubt sind: - -[{python}] ----- -detect = any_of( - startswith(".1.3.6.1.2.1.1.1.0", "foo version 3 system"), - startswith(".1.3.6.1.2.1.1.1.0", "foo version 4 system"), - startswith(".1.3.6.1.2.1.1.1.0", "foo version 4.1 system"), -) ----- - -Übrigens: Kennen Sie sich gut mit xref:regexes#[regulären Ausdrücken] aus? Dann würden Sie wahrscheinlich -das ganze vereinfachen und doch wieder mit einer Zeile auskommen: - -[{python}] ----- -detect = matches(".1.3.6.1.2.1.1.1.0", "FOO Version (3|4|4.1) .*"), ----- - -Und noch ein wichtiger Hinweis: Die OIDs, die Sie bei der `detect`-Deklaration -von einem Plugin angeben, werden im Zweifel von *jedem* Gerät geholt, welches -per SNMP überwacht wird. Seien Sie daher sehr sparsam bei der Verwendung von herstellerspezifischen -OIDs. Versuchen Sie, Ihre Erkennung unbedingt so zu machen, dass ausschließlich -die `sysDescr` (`.1.3.6.1.2.1.1.1.0`) und die `sysObjectID` -(`.1.3.6.1.2.1.1.2.0`) verwendet werden. Falls Sie dennoch eine weitere -andere OID benötigen, dann reduzieren Sie die Anzahl der Geräte, wo diese angefragt -wird, auf ein Minimum, indem Sie zuvor mittels der `sysDescr` so viele Geräte -wie möglich bereits ausschließen, z.B. so: - -[{python}] ----- -detect = all_of( - startswith(".1.3.6.1.2.1.1.1.0", "foo"), # first check sysDescr - contains(".1.3.6.1.4.1.4455.1.3", "bar"), # fetch vendor specific OID -) ----- - -Das `all_of()` funktioniert so, dass bei einem Scheitern der ersten -Bedingung die zweite gar nicht erst probiert wird (und somit die betreffende -OID auch nicht geholt). Hier im Beispiel wird die OID `.1.3.6.1.4.1.4455.1.3` nur -bei solchen Geräten geholt, die `foo` in ihrer `sysDescr` haben. - -Was geschieht, wenn Sie die Deklaration falsch oder zumindest nicht ganz -zielsicher gemacht haben? - -* Falls die Detection fälschlicherweise Geräte erkennt, auf denen die nötigen OIDs gar nicht vorhanden sind, wird Ihre Discovery-Funktion dann auch keine Services erzeugen. Es passiert also nichts „Schlimmes“. Allerdings wird das die Discovery auf solchen Geräten verlangsamen, da jetzt jedes mal nutzlos versucht wird, die entsprechenden OIDs abzufragen. -* Falls die Detection eigentlich zulässige Geräte _nicht_ erkennt, werden dort im Monitoring bei der Discovery auch keine Services gefunden. - -=== Die OID-Bereiche für das Monitoring - -Die wichtigste Stelle der SNMP-Deklaration ist die Angabe, welche OIDs für -das Monitoring geholt werden sollen. In fast allen Fällen benötigt ein Plugin -dazu nur ausgewählte Äste aus einer einzigen Tabelle. Betrachten wir folgendes -Beispiel: - -[{python}] ----- - fetch = SNMPTree( - base = '.1.3.6.1.4.1.35424.1.2', - oids = [ - '4.0', - '5.0', - '8.0', - ], - ), ----- - -Das Schlüsselwort `base` gibt hier einen OID-Präfix an. Alle -nötigen Daten liegen unterhalb. Bei `oids` geben Sie dann eine -Liste von Sub-OIDs an, die ab dort geholt werden sollen. In obigem -Beispiel werden dann insgesamt drei SNMP-Walks gemacht, nämlich -ausgehend von den OIDs `.1.3.6.1.4.1.35424.1.2.4.0`, `.1.3.6.1.4.1.35424.1.2.5.0` -und `.1.3.6.1.4.1.35424.1.2.8.0`. Dabei ist es wichtig, dass diese -Walks die gleiche Anzahl von Variablen holen und dass diese auch einander -entsprechen. Damit ist gemeint, dass z.B. das n-te Element aus jedem der -Walks dem selben überwachten Objekt entspricht. - -Hier ist ein Beispiel vom Check-Plugin `snmp_quantum_storage_info`: - -[{python}] ----- - tree = SNMPTree( - base=".1.3.6.1.4.1.2036.2.1.1", # qSystemInfo - oids=[ - "4", # qVendorID - "5", # qProdId - "6", # qProdRev - "12", # qSerialNumber - ], - ), -) ----- - -Hier wird pro Storage-Gerät jeweils die Vendor ID, die Product ID, die Product Revision -und die Seriennummer geholt. - -Der Discovery- und Check-Funktion werden diese Daten als Tabelle präsentiert, also -als Liste von Listen. Dabei wird die Tabelle so gespiegelt, dass Sie pro Eintrag -in der äußeren Liste alle Daten zu einem Item haben. Jeder Eintrag hat so viele -Elemente, wie Sie bei `oids` angegeben haben. So können Sie die Liste -sehr praktisch mit einer Schleife durchlaufen, z.B. - -[{python}] ----- - for vendor_id, prod_id, prod_rev, serial_number in section: - ... ----- - -Bitte beachten Sie: - -* Alle Einträge sind _strings_, selbst wenn die betreffenden OIDs eigentlich Zahlen sind. -* Fehlende OIDs werden als Leerstrings präsentiert -* Denken Sie an die Möglichkeit, während der Entwicklung mit `pprint` die Daten formatiert auszugeben. - -=== Weitere SNMP-Sonderheiten - -// COMMENT[Das fehlt alles noch, OIDCached("1.2.3"), OIDBytes("1.2.3")] - -Hier beschreiben wir in Zukunft noch: - -* Wie Sie mehrere unabhängige SNMP-Bereiche abrufen können -* Was es mit OIDEnd() auf sich hat -* Weitere Sonderfälle beim Umgang mit SNMP -//// - - - - -== Weitere Komponenten der Check-Entwicklung - -=== Metriken -// TK: alter Inhalt aus: 11. Angepasste Darstellung von Metriken - -//// -[#metrics] -== Angepasste Darstellung von Metriken - -=== Der Sinn von Metrikdefinitionen - -In unserem obigen Beispiel haben wir das Plugin `foobar` die Metrik `fooslots` -erzeugen lassen. Metriken werden in der grafischen Oberfläche von {CMK} sofort -sichtbar, ohne dass Sie etwas dafür tun müssten. -Pro Metrik wird bei den Servicedetails automatisch ein Graph erzeugt. - -Allerdings gibt es dabei ein paar Einschränkungen: - -* Es erscheint nicht automatisch ein „Perf-O-Meter“, also die grafische balkenartige Vorschau des Messwerts, wenn der Service in der Listendarstellung angezeigt wird (z.B. in der Ansicht, die alle Services eines Hosts darstellt). -* Es werden nicht automatisch passende Metriken in einem Graphen kombiniert, sondern jede erscheint einzeln. -* Die Metrik hat keinen richtigen Titel, sondern es wird der interne Variablenname der Metrik gezeigt. -* Es wird keine Einheit verwendet, die eine sinnvolle Darstellung erlaubt (z.B. GB anstelle von einzelnen Bytes) -* Es wir zufällig eine Farbe ausgewählt. - -Um die Darstellung Ihrer Metriken in diesen Belangen zu vervollständigen, -benötigen Sie noch einige Definitionen in einer weiteren Datei. - - -=== Vorhandene Metrikdefinitionen verwenden - -Bevor Sie das tun, sollten Sie -- ähnlich wie beim Regelsatz für -die Parameter -- zunächst prüfen, ob {CMK} nicht bereits eine geeignete -Metrikdefinition mitbringt. Die vordefinierten Metrikdefinitionen finden -Sie im Verzeichnis `~/lib/check_mk/gui/plugins/metrics/`. In der Datei `cpu.py` -finden Sie beispielsweise eine Metrik für freien Platz eines Dateisystems: - -[{python}] ----- -metric_info["util"] = { - "title": _("CPU utilization"), - "unit": "%", - "color": "26/a", -} ----- - -Falls diese für Ihr Plugin geeignet ist, müssen Sie lediglich in Ihrem -Aufruf der `Metric()`-Klasse den Namen `"util"` verwenden. -Alles andere leitet sich dann automatisch davon ab. - - -[#ownmetricdefinitions] -=== Eigene Metrikdefinitionen - -Falls keine passende Metrik dabei ist, legen Sie einfach selbst -eine an. In unserem Beispiel wollen wir einen eigene Metrik für -unsere `fooslots` definieren. Dazu legen wir eine Datei in -`~/local/share/check_mk/web/plugins/metrics` an: - -.~/local/share/check_mk/web/plugins/metrics/foobar_metric.py -[{python}] ----- -from cmk.gui.i18n import _ -from cmk.gui.plugins.metrics import metric_info - -metric_info["fooslots"] = { - "title": _("Used slots"), - "unit": "count", - "color": "15/a", -} ----- - -Dazu einige Hinweise: - -* Der Schlüssel (hier `"fooslots"`) ist der Metrikname und muss dem entsprechen, was die Check-Funktion ausgibt. -* Das Importieren und Verwenden des Unterstrichs für die Internationalisierung ist optional, wie bereits bei den Regeln besprochen. -* Welche Unit-Definitionen es gibt, erfahren Sie in der Datei `~/lib/check_mk/gui/plugins/metrics/unit.py`. -* Die Farbdefinition verwendet eine Palette. Zu jeder Palettenfarbe gibt es `/a` und `/b`. Dies sind zwei Schattierungen der gleichen Farbe. In den vorhandenen Definitionen werden Sie auch viele direkte Farbkodierungen wie `"#ff8800"` finden. Diese werden nach und nach abgeschafft und alle durch Palettenfarben ersetzt werden, da diese ein einheitlicheres Aussehen bieten und auch leichter an die Themes der Oberfläche angepasst werden können. - -Diese Definition sorgt jetzt dafür, dass Farbe, Titel und Einheit der Metrik -nach unsere Wünschen angezeigt werden. - - -=== Graphen mit mehreren Metriken - -Möchten Sie mehrere Metriken in einem Graphen kombinieren (was oft sehr sinnvoll ist), -benötigen Sie, einfach in der gleichen Datei, eine Graphdefinition. Dies geschieht über -das globale Dictionary `graph_info`. - -Nehmen wir dazu als Beispiel an, unser Check hätte zwei Metriken und zwar `fooslots` -und `fooslots_free`. Die Metrikdefinitionen wären z.B.: - -.~/local/share/check_mk/web/plugins/metrics/foobar_metric.py -[{python}] ----- -from cmk.gui.i18n import _ -from cmk.gui.plugins.metrics import ( - metric_info, - graph_info, -) - -metric_info["fooslots"] = { - "title": _("Used slots"), - "unit": "count", - "color": "16/a", -} - -metric_info["fooslots_free"] = { - "title": _("Free slots"), - "unit": "count", - "color": "24/a", -} ----- - -Nun fügen wir einen Graphen an, der diese beiden Metriken als Linien einzeichnet: - -[{python}] ----- -graph_info["fooslots_combined"] = { - "metrics": [ - ("fooslots", "line"), - ("fooslots_free", "line"), - ], -} ----- - -// Screenshot! - -Hinweise dazu: - -* Leider gibt es im Handbuch noch keine Beschreibung der Möglichkeiten dieser Definition. Aber Sie finden sehr viele Beispiele in den Dateien im Verzeichnis `~/lib/check_mk/gui/plugins/metrics`. -* Probieren Sie anstelle von `line` auch mal `area` oder `stack`. - -// Screenshots! - - -[#perfometer] -=== Darstellung der Metriken im Perf-O-Meter - -Möchten Sie zu unserer Metrik noch ein Perf-O-Meter in der -Servicezeile anzeigen, benötigen Sie eine weitere Datei, diesmal -im Verzeichnis `~/local/share/check_mk/web/plugins/perfometer`. - -Beispiel: - -// Screenshot! - -.~/local/share/check_mk/web/plugins/perfometer/foobar_perfometer.py -[{python}] ----- -from cmk.gui.plugins.metrics import perfometer_info - -perfometer_info.append({ - "type": "logarithmic", - "metric": "fooslots", - "half_value": 5, - "exponent": 2.0, -}) ----- - -Perf-O-Meter sind etwas trickreicher als Graphen, da es keine Legende -gibt. Und deswegen ist das mit dem Wertebereich schwierig. Da das arme -Perf-O-Meter nicht wissen kann, welche Messwerte denn überhaupt möglich -sind und der Platz sehr begrenzt ist, verwenden viele eingebaute Check-Plugins -eine logarithmische Darstellung. Dies ist auch in unserem Beispiel so. -`half_value` ist der Messwert, welcher genau in der Mitte des -Perf-O-Meters angezeigt wird. Bei einem Wert von `5`, wäre also -hier der Balken halb gefüllt. Und `exponent` beschreibt den -Faktor, welcher notwendig ist, damit weitere 10{nbsp} des Bereichs gefüllt würden. -Also würde hier im Beispiel ein Messwert von `10` bei 60{nbsp}% und einer -von `20` bei 70{nbsp}% angezeigt werden. - -Der Vorteil von dieser Methode: Wenn Sie eine Liste von Services gleicher -Art haben, können Sie alle Perf-O-Meter untereinander optisch schnell -vergleichen, da alle die gleiche Skala haben. Und trotz der sehr kleinen -Darstellungen kann man sowohl bei sehr kleinen als auch bei der großen -Messwerten die Unterschiede gut erkennen. Dafür sind die Werte allerdings nicht -maßstabsgetreu. - -Alternativ können Sie auch ein lineares Perf-O-Meter verwenden. Das -ist immer dann sinnvoll, wenn es einen bekannten Maximalwert gibt. -Ein typischer Fall sind Messwerte, welche Prozente von 0 bis 100 -darstellen. Das sähe dann z.B. so aus: - -[{python}] ----- -perfometer_info.append({ - "type": "linear", - "segments": ["fooslots_used_percent"], - "total": 100.0, -}) ----- - -Hier gibt es noch einen weiteren Unterschied zur logarithmischen -Darstellung: `segments` ist hier eine Liste und erlaubt das -nebeneinander Darstellen von mehreren Metriken. - -Wie immer finden Sie Beispiele in den vielen von {CMK} ausgelieferten -Plugins. Diese sind ebenfalls in den Dateien im Verzeichnis -`~/lib/check_mk/gui/plugins/metrics`. -//// - - - - - -=== Formatierung von Zahlen -// TK: alter Inhalt aus: 9. Formatierung von Zahlen - -//// -[#format_numbers] -== Formatierung von Zahlen - -=== Grundlegendes - -In der Summary oder den Details eines Services werden oft Zahlen ausgegeben. Um Ihnen -eine schöne und korrekte Formatierung möglichst einfach zu machen, und um -auch die Ausgaben von allen Check-Plugins zu vereinheitlichen, gibt es Hilfsfunktionen -für die Darstellung von verschiedenen Arten von Größen. -Alle diese sind Unterfunktionen vom Modul `render` und werden folglich -mit `render.` aufgerufen. Z.B. ergibt `render.bytes(2000)` den Text -`1.95 KiB`. - -Allen diesen Funktionen ist gemein, dass Sie ihren Wert in einer -sogenannten _kanonischen_ oder natürlichen Einheit bekommen. So muss -man nie nachdenken und es gibt keine Schwierigkeiten oder Fehler bei der -Umrechnung. Z.B. werden Zeiten immer in Sekunden angegeben und Größen von -Festplatten, Dateien, etc. immer in Bytes und nicht in Kilobytes, Kibibytes, -Blöcken oder sonstigem Durcheinander. - -Bitte verwenden Sie diese Funktionen auch dann, wenn Ihnen die Darstellung -nicht so gut gefällt. Immerhin ist diese dann für den Benutzer einheitlich. -Und zukünftige Versionen von {CMK} können die Darstellung möglicherweise -ändern oder sogar konfigurierbar für den Benutzer machen. Davon wird dann -Ihr Check-Plugin auch profitieren. - -Nach der ausführlichen Beschreibung aller Darstellungsfunktionen (Renderfunktionen) finden -Sie eine Zusammenfassung in Form einer übersichtlichen Tabelle. - -=== Zeiten, Zeitspannen, Frequenzen - -Absolute Zeitangaben (Zeitstempel) werden mit `render.date()` oder `render.datetime()` -formatiert. Die Angaben erfolgen immer in _Sekunden ab dem 1. Januar 1970, 00:00:00 UTC_ -- der -sogenannten Epochenzeit. Dies ist auch das Format, mit dem die Pythonfunktion `time.time()` -arbeitet. Vorteil an dieser Darstellung ist, dass sich damit sehr einfach rechnen lässt, also -z.B. die Dauer eines Vorgangs, wenn Start- und Endzeit bekannt sind. Die Formel ist dann -einfach `duration = end - start`. Und diese Berechnungen funktionieren unabhängig von -der Zeitzone, Sommerzeitumstellungen oder Schaltjahren. - -`render.date()` gibt dabei nur das Datum aus, `render.datetime()` fügt noch die -Uhrzeit hinzu. Die Ausgabe erfolgt dabei gemäß der aktuellen Zeitzone desjenigen {CMK}-Servers, -welcher den Check ausführt! Beispiele: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.date(0)` |`Jan 01 1970` -|`render.datetime(0)` |`Jan 01 1970 01:00:00` -|`render.date(1600000000)` |`Sep 13 2020` -|`render.datetime(1600000000)` |`Sep 13 2020 14:26:40` -|=== - - -Bitte wundern Sie sich jetzt nicht, dass `render.date(0)` als Uhrzeit -nicht 00:00, sondern 01:00 ausgibt! Das liegt daran, dass wir dieses Handbuch -in der Zeitzone von Deutschland schreiben, und die ist der Standardzeit UTC -eine Stunde voraus (zumindest während der Normalzeit, denn der 1. Januar liegt -ja bekanntlich nicht in der Sommerzeit). - -Für _Zeitspannen_ gibt es noch die Funktion `render.timespan()`. -Diese bekommt eine Dauer in Sekunden und gibt das menschenlesbar aus. Bei -größeren Zeitspannen werden Sekunden oder Minuten weggelassen. - -// COMMENT[auch ein kleines Beispiel? Geht auch für "zeptoseconds" :-)] - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.timespan(1)` |`1 second` -|`render.timespan(123)` |`2 minutes 3 seconds` -|`render.timespan(12345)` |`3 hours 25 minutes` -|`render.timespan(1234567)` |`14 days 6 hours` -|=== - - -Eine _Frequenz_ ist quasi der Kehrwert der Zeit. Die kanonische Einheit ist _Hz_, was -das gleiche bedeutet wie 1 / sec. Einsatzgebiet ist z.B. die Taktrate einer CPU: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.frequency(111222333444)` |`111 GHz` -|=== - - - -=== Bytes - -Überall wo es um Arbeitsspeicher, Dateien, Festplatten, Dateisysteme und -dergleichen geht, ist die kanonische Einheit das _Byte_. Da Computer -so etwas meist in Zweierpotenzen organisieren, also z.B. in Einheiten zu -512, 1024 oder 65536 Bytes, hatte sich dabei von Beginn an eingebürgert, -dass ein _Kilobyte_ nicht 1000, sondern 1024 Bytes ist. An sich -sehr praktisch, weil so meist runde Zahlen rauskamen. Der legendäre Commodore -C64 hatte eben 64 Kilobyte Speicher und nicht 65,536. - -Leider kamen irgendwann Festplattenhersteller auf die Idee, die Größen -ihrer Platten in 1000'er-Einheiten anzugeben. Da bei jeder Größenordnung -der Unterschied zwischen 1000 und 1024 immerhin 2,4{nbsp}% ausmacht, und diese sich -aufmultiplizieren, wird so aus einer Platte der Größe 1 GB (1024 mal 1024 * -1024) auf einmal 1,07 GB. Das verkauft sich besser. - -Diese lästige Verwirrung besteht bis heute und sorgt immer wieder für Fehler. -Als Linderung wurden von der internationalen elektrotechnischen Kommission -neue Präfixe auf Grundlage des Binärsystems festgelegt. Demnach ist heute -offiziell ein Kilobyte 1000 Byte und ein _Kibibyte_ 1024 Byte (2 hoch 10). -Außerdem soll man _Mebibyte_ und _Gibitbyte_ und _Tebibyte_ -sagen (schon mal gehört?). Die Abkürzungen lauten (Achtung, hier auf -einmal immer i, statt e!) _KiB_, _MiB_, _GiB_ und _TiB_. - -{CMK} passt sich an diesen Standard an und hilft Ihnen mit mehreren angepassten -Renderfunktionen dabei, dass Sie immer korrekte Ausgaben machen. So -gibt es speziell für Festplatten und Dateisysteme die Funktion -`render.disksize()`, welche die Ausgabe in 1000'er-Potenzen macht. - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.disksize(1000)` |`1.00 kB` -|`render.disksize(1024)` |`1.02 kB` -|`render.disksize(2000000)` |`2.00 MB` -|=== - - -Bei der Größe von _Dateien_ ist es oft üblich, die genaue Größe in -Bytes _ohne Rundung_ anzugeben. Dies hat den Vorteil, dass man so -sehr schnell sehen kann, wenn sich eine Datei auch nur minimal geändert -hat oder dass zwei Dateien (wahrscheinlich) gleich sind. Hierfür ist -die Funktion `render.filesize()` verantwortlich: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.filesize(1000)` |`1,000 B` -|`render.filesize(1024)` |`1,024 B` -|`render.filesize(2000000)` |`2,000,000 B` -|=== - - -Wenn Sie eine Größe ausgeben möchten, die keine Platten- oder Dateigröße ist, -dann verwenden Sie einfach das generische `render.bytes()`. Hier bekommen -sie die Ausgabe in klassischen 1024'er-Potenzen in der neuen offiziellen Schreibweise: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.bytes(1000)` |`1000 B` -|`render.bytes(1024)` |`1.00 KiB` -|`render.bytes(2000000)` |`1.91 MiB` -|=== - - - -=== Bandbreiten, Datenraten - -Die Netzwerker haben ihre eigenen Begriffe und Arten, Dinge auszudrücken. -Und wie immer gibt sich {CMK} Mühe, in jeder Domäne, die dort übliche -Art zu kommunizieren, zu übernehmen. Deswegen gibt es für Datenraten -und Geschwindigkeiten gleich drei verschiedene Renderfunktionen. Alle -haben gemeinsam, dass die Raten in _Bytes pro Sekunde_ übergeben -werden, selbst dann, wenn die Ausgabe in Bits erfolgt! - -`render.nicspeed()` stellt die Maximalgeschwindigkeit einer -Netzwerkkarte oder eines Switchports dar. Da es keine Messwerte sind, muss -auch nicht gerundet werden. Obwohl kein Port einzelne Bits versenden -kann, sind die Angaben aus historischen Gründen in Bits. Achtung: trotzdem -müssen Sie auch hier Bytes pro Sekunde übergeben! Beispiele: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.nicspeed(12500000)` |`100 MBit/s` -|`render.nicspeed(100000000)` |`800 MBit/s` -|=== - - -`render.networkbandwidth()` ist für eine tatsächlich gemessene -Übertragungsgeschwindigkeit im Netzwerk. Eingabewert sind wieder Bytes pro -Sekunde (Oder „Oktette“, wie der Netzwerker sagen würde): - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.networkbandwidth(123)` |`984 Bit/s` -|`render.networkbandwidth(123456)` |`988 kBit/s` -|`render.networkbandwidth(123456789)` |`988 MBit/s` -|=== - - -Wo es nicht ums Netzwerk geht und dennoch Datenraten ausgegeben werden, -sind wieder Bytes üblich. Prominentester Fall sind IO-Raten von Festplatten. -Dafür gibt es die Renderfunktion `render.iobandwidth()`, die in -{CMK} mit 1000'er-Potenzen arbeitet: - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.iobandwidth(123)` |`123 B/s` -|`render.iobandwidth(123456)` |`123 kB/s` -|`render.iobandwidth(123456789)` |`123 MB/s` -|=== - - -=== Prozentwerte - -Die Funktion `render.percent()` stellt einen Prozentwert dar -- -auf zwei Nachkommastellen gerundet. Es ist insofern eine Ausnahme zu -den anderen Funktionen, als hier nicht der eigentlich natürliche Wert --- also das Verhältnis -- übergeben wird, sondern wirklich die Prozentzahl. -Wenn also etwas z.B. zur Hälfte voll ist, müssen Sie nicht 0.5 sondern 50 übergeben. - -Weil es manchmal interessant sein kann zu wissen, ob ein Wert beinahe Null -oder exakt Null ist, werden Werte durch Anfügen eines „<“ Zeichens markiert, -die größer als Null, aber kleiner als 0.01 sind. - -[cols="50,~"] -|=== -|Aufruf |Ausgabe - -|`render.percent(0.004)` |`<0.01{nbsp}%` -|`render.percent(18.5)` |`18.50{nbsp}%` -|`render.percent(123)` |`123.00{nbsp}%` -|=== - - -=== Zusammenfassung - -Hier ist nochmal eine Übersicht über alle Renderfunktionen: - -[cols="10,10,40,~"] -|=== -|Funktion |Eingabe |Beschreibung |Beispielausgabe - -|`date` |Epoche |Datum |`Dec 18 1970` -|`datetime` |Epoche |Datum und Uhrzeit |`Dec 18 1970 10:40:00` -|`timespan` |Sekunden |Dauer / Alter |`3d 5m` -|`frequency` |Hz |Frequenz (z.B. Taktrate) |`110 MHz` -|`disksize` |Bytes |Größe von Festplatte, Basis 1000 |`1,234 GB` -|`filesize` |Bytes |Größe von Dateien, volle Genauigkeit |`1,334,560 B` -|`bytes` |Bytes |Größe in Bytes, Basis 1024 |`23,4 KiB` -|`nicspeed` |Octets/sec |Geschwindigkeit von Netzwerkkarten |`100 MBit/s` -|`networkbandwidth` |Octets/sec |Übertragungsgeschwindigkeit |`23.50 GBit/s` -|`iobandwidth` |Bytes/sec |IO-Bandbreiten |`124 MB/s` -|`percent` |Prozent |Prozentwert, sinnvoll gerundet |`99.997{nbsp}%` -|=== -//// - - - -=== Agentenausgabe parsen -// TK: alter Inhalt aus: 13. Komplexe Agentenausgaben mittels Parse-Funktion bändigen - -//// -[#parsefunction] -== Komplexe Agentenausgaben mittels Parse-Funktion bändigen - -Der nächste Schritt ist die sogenannten _Parse-Funktion_. Diese -hat die Aufgabe, die „rohen“ Agentendaten zu parsen und in eine logisch -aufgeräumte Form zu bringen, die für alle weiteren Schritte einfach -zu verarbeiten ist. Konvention ist, dass diese nach der Agentensektion -benannt wird und mit `parse_` beginnt. Sie bekommt als einziges -Argument `string_table`. Bitte beachten Sie, dass Sie hier nicht -frei in der Wahl des Arguments sind. Es muss wirklich so heißen. - -Wir schreiben unsere Parse-Funktion jetzt erstmal so, dass wir einfach -nur die Daten, die sie bekommt, auf der Konsole ausgeben. Dazu nehmen -wir einfach die `print`-Funktion (Achtung: seit Python 3 sind -hier Klammern zwingend notwendig): - -[{python}] ----- -def parse_linux_usbstick(string_table): - print(string_table) ----- - -Damit das Ganze irgendetwas bewirken soll, müssen wir unsere Parse-Funktion -und überhaupt die neue Agentensektion bei {CMK} bekannt machen. Dazu -rufen wir eine Registrierfunktion auf: - -[{python}] ----- -register.agent_section( - name = "linux_usbstick", - parse_function = parse_linux_usbstick, -) ----- - -Hier ist es wichtig, dass der Name der Sektion wirklich exakt mit dem -Sektions-Header in der Agentenausgabe übereinstimmt. Insgesamt -sieht das jetzt so aus: - -.local/lib/check_mk/base/plugins/agent_based/linux_usbstick.py -[{python}] ----- -from .agent_based_api.v1 import * - -def parse_linux_usbstick(string_table): - print(string_table) - return string_table - -register.agent_section( - name = "linux_usbstick", - parse_function = parse_linux_usbstick, -) ----- - -Von diesem Moment an bekommt jedes Plugin, das die Section `linux_usbstick` -benutzt, den Rückgabewert der Parse-Funktion übergeben. In der Regel wird das das -gleichnamige Check-Plugin sein. - -Wir haben jetzt gewissermaßen das einfachste mögliche Plugin gebaut, was noch -keinen wirklich Nutzen hat, aber das wir immerhin schon testen können. Dazu -stoßen wir auf der Kommandozeile eine Serviceerkennung (Option `-I`) -von dem Host an, dessen Agenten wir vorhin präpariert haben. _Wenn_ -dessen Ausgabe auch wirklich eine Sektion `linux_usbstick` enthält, -dann müssten wir unsere Debugausgabe sehen: - -[{shell}] ----- -{c-omd} cmk -I myhost123 -[['ata-APPLE_SSD_SM0512F_S1K5NYBF810191'], ['wwn-0x5002538655584d30']] ----- - -Etwas übersichtlicher wird die Ausgabe, wenn wir das einfache `print` -durch ein Pretty-print aus dem Modul `pprint` ersetzen. Das ist für -alle weitere Debugausgaben sehr empfehlenswert: - -.~/local/lib/check_mk/base/plugins/agent_based/linux_usbstick.py -[{python}] ----- -from .agent_based_api.v1 import * -*import pprint* - -def parse_linux_usbstick(string_table): - *pprint.pprint(string_table)* - return string_table - -register.agent_section( - name = "linux_usbstick", - parse_function = parse_linux_usbstick, -) ----- - -Das sieht dann so aus: - -[{shell}] ----- -{c-omd} cmk -I myhost123 -[['ata-APPLE_SSD_SM0512F_S1K5NYBF810191'], - ['wwn-0x5002538655584d30']] ----- - - - -=== Die Parse-Funktion schreiben - -Wenn Sie genau hinsehen, dann erkennen Sie, dass es sich hier um verschachtelte -Listen handelt. Im Argument `string_table` bekommen Sie eine Liste, -welche _pro Zeile_ der Agentenausgabe eine Liste von _Worten_ -beinhaltet. Dabei werden die Zeilen an Folgen von Leerzeichen getrennt. Da -unsere Sektion pro Zeile nur ein Wort enthält, bestehen ergo die inneren -Listen aus nur jeweils einem Eintrag. - -Folgendes Beispiel macht die Struktur noch etwas klarer: - -.~/local/lib/check_mk/base/plugins/agent_based/linux_usbstick.py -[{python}] ----- -from .agent_based_api.v1 import * -import pprint - -def parse_linux_usbstick(string_table): - print("Number of lines: %d" % len(string_table)) - print("Number of words in first line: %d" % len(string_table[0])) - print("Length of first word: %d" % len(string_table[0][0])) - return string_table - -register.agent_section( - name = "linux_usbstick", - parse_function = parse_linux_usbstick, -) ----- - -Die Ausgabe sieht dann so aus: - -[{shell}] ----- -{c-omd} cmk -I myhost123 -Number of lines: 3 -Number of words in first line: 1 -Length of first word: 36 ----- - -Für unser Beispiel benötigen wir einfach nur eine einfache Liste der Devicenamen. -Also machen wir unsere Parse-Funktion so, dass sie aus jeder Zeile das eine Wort -auspackt und in eine hübsche neue Liste verpackt: - -[{python}] ----- -def parse_linux_usbstick(string_table): - parsed = [] - for line in string_table: - parsed.append(line[0]) - pprint.pprint(parsed) - return string_table ----- - -Die Debugausgabe sieht dann so aus (bitte schauen Sie genau hin, es -gibt jetzt nur noch ein einziges paar eckiger Klammern): - -[{python}] ----- -['ata-APPLE_SSD_SM0512F_S1K5NYBF810191', - 'wwn-0x5002538655584d30'] ----- - -Damit die Parse-Funktion vollständig ist, müssen wir jetzt noch die -Debugmeldung entfernen und -- ganz wichtig -- das neue Ergebnis mit -`return` zurückgeben: - -[{python}] ----- -def parse_linux_usbstick(string_table): - parsed = [] - for line in string_table: - parsed.append(line[0]) - return parsed ----- - -Natürlich müssen von diesem Moment an alle betroffenen Plugins mit dem neuen -Datenformat arbeiten können. -//// - - - - -== Probleme identifizieren und lösen - -=== Fehlerbehandlung -// TK: alter Inhalt aus: 7. Fehlerbehandlung - -//// -[#errors] -== Fehlerbehandlung - -=== Exceptions und Crashreports - -Die korrekte Behandlung von Fehlern nimmt (leider) einen großen Teil der -Programmierarbeit ein. Die gute Nachricht ist: die API von {CMK} erledigt dabei -bereits die meiste Arbeit. Meistens ist für Sie daher wichtig, dass Sie Fehler -einfach *gar nicht* behandeln. - -Wenn Python in eine Situation kommt, die in irgendeiner Form _unerwartet_ ist, -reagiert es mit einer sogenannten _Exception_. Hier sind ein paar Beispiele: - -* Sie konvertieren mit `int(...)` einen String in eine Zahl, aber der String enthält keine Zahl, z.B. `int("foo")` -* Sie greifen mit `bar[4]` auf das fünfte Element von `bar` zu, aber das hat nur vier Elemente. -* Sie rufen eine Funktion auf, die es nicht gibt. - -Hier gilt die generelle wichtige Regel: *Fangen Sie Exceptions nicht selbst ab!* Denn {CMK} übernimmt das für -Sie in einer sinnvollen immer gleichen Art und Weise. Und zwar meist mit einem _Crashreport_. Das sieht -dann z.B. so aus: - -image::crash_report_1.png[] - -Durch einen Klick auf das Icon icon:icon_crash[] gelangt der Anwender dann auf eine Seite, auf der er: - -* die Datei angezeigt bekommt, in der der Crash stattgefunden hat; -* alle Informationen über den Crash angezeigt bekommt (wie Fehlermeldung, Aufrufstack, Agentenausgabe, aktuelle Werte von lokalen Variablen und vieles mehr); -* den Report zu uns ({comfull}) als Feedback einsenden kann. - -Das Einsenden des Reports macht natürlich nur Sinn für Check-Plugins, -welche offiziell Teil von {CMK} sind. Aber Sie können Ihre Anwender bitten, -Ihnen die Daten einfach zukommen zu lassen. Diese werden Ihnen beim Finden -des Fehlers helfen. Oft ist es ja so, dass das Check-Plugin bei Ihnen selbst -funktioniert, aber es bei anderen Anwendern vielleicht sehr sporadisch zu -Fehlern kommt. Diese können Sie dann so meist sehr leicht finden. - -Falls Sie stattdessen die Exception selbst abfangen würden, wären diese ganzen -Informationen nicht verfügbar. Sie würden vielleicht den Service -auf {UNKNOWN} setzen und eine Fehlermeldung ausgeben. Aber die ganzen Umstände, -wie es dazu kam (z.B. die Daten vom Agenten), wäre verschleiert. - -=== Exceptions auf der Kommandozeile ansehen - -Falls Sie ihr Plugin auf der Kommandozeile ausführen, werden keine Crashreports -erzeugt. Sie sehen nur die zusammengefasste Fehlermeldung: - -[{shell}] ----- -{c-omd} cmk -II --detect-plugins=foobar myhost123 - WARNING: Exception in discovery function of check plugin 'foobar': invalid literal for int() with base 10: 'foo' ----- - -Aber: hängen Sie einfach die Option `--debug` dran. Dann bekommt Sie den -Python-Stacktrace: - -[{shell}] ----- -{c-omd} cmk --debug -II --detect-plugins=foobar myhost123 -Traceback (most recent call last): - File "/omd/sites/myhost123/bin/cmk", line 82, in - exit_status = modes.call(mode_name, mode_args, opts, args) - File "/omd/sites/myhost123/lib/python3/cmk/base/modes/__init__.py", line 68, in call - return handler(*handler_args) - File "/omd/sites/myhost123/lib/python3/cmk/base/modes/check_mk.py", line 1577, in mode_discover - discovery.do_discovery(set(hostnames), options.get("checks"), options["discover"] == 1) - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 345, in do_discovery - _do_discovery_for( - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 397, in _do_discovery_for - discovered_services = _discover_services( - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 1265, in _discover_services - service_table.update({ - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 1265, in - service_table.update({ - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 1337, in _execute_discovery - yield from _enriched_discovered_services(hostname, check_plugin.name, plugins_services) - File "/omd/sites/myhost123/lib/python3/cmk/base/discovery.py", line 1351, in _enriched_discovered_services - for service in plugins_services: - File "/omd/sites/myhost123/lib/python3/cmk/base/api/agent_based/register/check_plugins.py", line 69, in filtered_generator - for element in generator(*args, **kwargs): - File "/omd/sites/myhost123/local/lib/python3/cmk/base/plugins/agent_based/foobar.py", line 5, in discover_foobar - int("foo") -ValueError: invalid literal for int() with base 10: 'foo' ----- - - -=== Ungültige Ausgaben vom Agenten - -Die Frage ist, wie Sie reagieren sollen, wenn die Ausgaben vom Agenten -nicht die Form haben, die Sie eigentlich erwarten würden - egal ob es der -„echte“ Agent ist oder die Daten per xref:snmp#[SNMP] kommen. Nehmen wir an, dass Sie -pro Zeile immer drei Worte erwarten. Was sollen Sie tun, falls nur zwei kommen? - -Nun -- wenn das ein _erlaubtes und bekanntes_ Verhalten des Agenten -ist, dann müssen Sie das natürlich abfangen und mit einer Fallunterscheidung -arbeiten. - -Falls das aber eigentlich nicht sein darf ... dann tun Sie am besten so, -als ob die Zeile immer aus drei Worten besteht, also z.B. mit: - -[{python}] ----- -def check_foobar(section): - for foo, bar, baz in section: - # ... ----- - -Sollte jetzt mal eine Zeile dabei sein, die nicht aus genau drei Worten -besteht, wird eine hübsche Exception erzeugt und Sie bekommen den gerade -erwähnten sehr hilfreichen Crashreport. - - -=== Fehlende Items - -Was ist, wenn der Agent korrekte Daten ausgibt, aber das Item fehlt, das -überprüft werden soll? Also z.B. auf diese Art: - -[{python}] ----- -def check_foobar(item, section): - for sector, used, slots in section: - if item == sector: - # ... Check state ... - yield Result(...) - return ----- - -Ist das gesuchte Item nicht dabei, so wird die Schleife durchlaufen und -Python fällt am Ende der Funktion einfach hinten raus, ohne dass ein -Resultat „geyieldet“ wurde. Und das ist genau das Richtige! Denn daran -erkennt {CMK}, dass das zu überwachende Item fehlt und erzeugt mit {UNKNOWN} -den richtigen Status und einen passenden Standardtext dazu. -//// - - - -== Anhang - -[#check_api_old] -=== Die alte Check-API -// TK: alter Inhalt aus: 1.2. Was hat sich seit der alten API geändert? - -//// -=== Was hat sich seit der alten API geändert? - -Haben Sie schon Erfahrung mit dem Entwickeln von Check-Plugins für die -{CMK}-Version {v16} oder früher? Dann finden Sie hier eine knappe -Übersicht über alle Änderungen, welche die ab {v20} verfügbare neue -Check-API mit sich bringt: - -* Plugins sind jetzt Python-3-Module und die Dateien müssen mit `.py` enden. -* Die eigenen Plugins liegen jetzt im Verzeichnis `local/lib/check_mk/base/plugins/agent_based`. -* Am Anfang der Datei brauchen Sie nun mindestens eine spezielle `import`-Anweisung. -* Die Sektionen und die eigentlichen Checks werden getrennt registriert. Dazu gibt es die neuen Funktionen `register.agent_section` und `register.check_plugin`. -* Etliche Funktions- und Argumentnamen wurden umbenannt. Unter anderem wird jetzt immer konsequent von _Discovery_ gesprochen (früher: _Inventory_). -* Die Discovery-Funktion (vormals Inventory-Funktion) und auch die Check-Funktion müssen nun _immer_ als Generatoren arbeiten (also `yield` verwenden). -* Die Namen der Argumente der deklarierten Funktionen sind jetzt fest vorgegeben. -* Anstelle der SNMP-Scanfunktion schreiben Sie eine _Deklaration_, welche OIDs mit welchen Werten erwartet werden. -* Die Funktionen zum Darstellen von Zahlen wurden neu strukturiert (z.B. wird `get_bytes_human_readable` zu `render.bytes`). -* Es gibt nun eine eigene Methode, mit der Checks andere ausschließen können (`superseeds`). Das wird nicht mehr in der SNMP-Scanfunktion gemacht. -* Die Hilfsfunktionen für die Arbeit mit Countern, Raten und Durchschnitten haben sich geändert. -* Anstelle von magischen Rückgabewerten wie z.B. `2` für {CRIT} gibt es jetzt Konstanten (z.B. `State.CRIT`). -* Viele mögliche Programmierfehler in Ihrem Plugin erkennt {CMK} jetzt sehr früh und kann Sie gleich darauf hinweisen. -//// - - -// TK: alter Inhalt aus: 1.3. Wird die alte API noch unterstützt? - -//// -=== Wird die alte API noch unterstützt? - -Ja, die bis zu Version {v16} von {CMK} gültige API für die -Entwicklung von Check-Plugins wird mit einigen kleinen Einschränkungen noch -etliche Jahre unterstützt werden, da mit ihr sehr sehr viele Plugins -entwickelt wurden. Während dieser Zeit wird {CMK} beide APIs parallel anbieten. -Einzelheiten erfahren Sie in Werk link:https://checkmk.com/werk/10601[#10601.^] - -Trotzdem empfehlen wir für die Entwicklung von neuen Plugins die neue API, -da diese konsistenter und logischer ist, besser dokumentiert und langfristig -zukunftssicher. -//// - -// TK: alter Inhalt aus: 12. Hinweise für Nutzer der alten API - -//// -[#old_api] -== Hinweise für Nutzer der alten API - -Sind Sie bereits erfahren bei der Entwicklung von Check-Plugins mit der bisherigen -API -- derjenigen bis Version {v16} von {CMK}? Dann finden Sie hier einige -Hinweise über wichtige Änderungen zusammengefasst. - - -=== saveint() und savefloat() - -Die beiden Funktionen `saveint()` und `savefloat()` sind weggefallen. -Zur Erinnerung: `saveint(x)` liefert `0` wenn sich `x` nicht -vernünftig in eine Zahl konvertieren lässt, z.B. weil es ein leerer String ist oder -nicht nur aus Ziffern besteht. - -Auch wenn es dafür einige wenige gute Anwendungsfälle gab, wurde es doch in der -Mehrheit der Fälle falsch verwendet und hat dazu geführt, dass so viele -xref:devel_check_plugins#errors[Fehler] verschleiert wurden. - -Für den Fall, dass Sie bei einem Leerstring eine `0` bekommen möchten, -also den häufigsten „guten“ Anwendungsfall von `saveint(x)`, können -Sie einfach Folgendes schreiben: - -[{python}] ----- -foo = int(x) if x else 0 ----- - -Für `savefloat()` gilt alles analog. -//// - - - - -=== ValueSpecs -// TK: alter Inhalt aus: 10.5. Weitere ValueSpecs - -//// -=== Weitere ValueSpecs - -In {CMK} gibt es zahlreiche ValueSpecs für alle möglichen Situationen. -Hier sind noch ein paar nützliche: - - -==== Float - -`Float` ist wie `Integer`, erlaubt aber die Eingabe -von Zahlen mit Nachkommastellen. - - -==== Percentage - -Oft möchte man Schwellen nicht in absoluten Zahlen, sondern in Prozent angeben. -Dazu gibt es das ValueSpec `Percentage`: - -[{python}] ----- -def _parameter_valuespec_foobar(): - return Dictionary( - elements=[ - ("levels_percent", Tuple( - title=_("Relative levels"), - elements=[ - Percentage(title=_("Warning at"), default_value=80), - Percentage(title=_("Critical at"), default_value=90) - ], - )), - ], - ) ----- - -Bei dieser ValueSpec würde das Check-Plugin die Parameter `{"levels_percent": -(80.0, 90.0)}` übergeben bekommen. - - -==== MonitoringState - -Der `MonitoringState` ist nützlich, wenn Sie dem Benutzer erlauben -wollen, für verschiedene Situationen jeweils einen der Zustände {OK}, -{WARN}, {CRIT} und {UNKNOWN} auszuwählen. Es bietet dem Benutzer ein -Drop-down-Feld mit eben diesen vier Möglichkeiten, welche dann umgesetzt -werden in eine der Zahlen `0`, `1`, `2` oder `3`. - -Hier können Sie z.B. einstellen, welchen Zustand der Service bekommen soll, -falls kein Backup konfiguriert bzw. vorhanden ist: - -[{python}] ----- -def _parameter_valuespec_plesk_backups(): - return Dictionary( - help=_("This check monitors backups configured for domains in plesk."), - elements=[ - ("no_backup_configured_state", - MonitoringState(title=_("State when no backup is configured"), default_value=1)), - ("no_backup_found_state", - MonitoringState(title=_("State when no backup can be found"), default_value=1)), - ... ----- - -Bei dieser ValueSpec würde das Check-Plugin die Parameter -`{"no_backup_configured_state": 1, "no_backup_found_state": 1}` übergeben -bekommen, falls in beiden Fällen der Default von {WARN} (=1) übernommen wurde. -Sie können die Zahl einfach in ein `State` Objekt umwandeln, indem Sie es -der Funktion `State()` übergeben: - -[{python}] ----- - yield Result( - state=State(params["no_backup_configured_state"]), - summary="No backup is configured!", - ) ----- - - -==== Age - -Das Feld `Age` erlaubt die Eingabe eines Alters, welches intern -als Anzahl von Sekunden gespeichert und übergeben wird: - -[{python}] ----- -def _parameter_valuespec_antivir_update_age(): - return Tuple(elements=[ - Age(title=_("Warning level for time since last update")), - Age(title=_("Critical level for time since last update")), - ],) ----- - - -==== Filesize - -Die ValueSpec `Filesize` erlaubt die Eingabe von Datei- (oder -Festplatten)größen. Intern wird mit Bytes gerechnet, aber der Benutzer -darf aus KB, MB, GB oder TB auswählen: - -[{python}] ----- - Tuple( - title=_("Maximum size of all files on backup space"), - help=_("The maximum size of all files on the backup space. " - "This might be set to the allowed quotas on the configured " - "FTP server to be notified if the space limit is reached."), - elements=[ - Filesize(title=_("Warning at")), - Filesize(title=_("Critical at")), - ], - ), ----- - -Das Thema ValueSpecs ist extrem flexibel und umfangreich und -würde diesen Artikel sprengen. Bitte schauen Sie sich die -Beispiele der von {CMK} mitausgelieferten Regeldefinitionen in -`lib/check_mk/gui/plugins/wato/check_parameters/` an. Dort gibt es -mehr als 500 Dateien mit Beispielen. -//// - - -[#files] -=== Dateien und Verzeichnisse -// TK: alter Inhalt aus: 15. Dateien und Verzeichnisse - -[cols="45,~",options="header"] -|=== -|Pfad |Bedeutung -|`~/local/lib/check_mk/base/plugins/agent_based/` |Ablageort für selbst geschriebene Check-Plugins. -|`~/local/share/check_mk/web/plugins/wato/` |Ablageort für Ihre Regelsätze für Check-Parameter. -|`~/local/share/check_mk/web/plugins/metrics/` |Ablageort für eigene Metrikdefinitionen. -|`~/local/share/check_mk/web/plugins/perfometer/` |Ablageort für eigene Definitionen von Perf-O-Metern. -|`~/local/share/check_mk/mibs/` |Legen Sie hier SNMP-MIB-Dateien ab, die automatisch geladen werden sollen. -|`~/lib/check_mk/gui/plugins/wato/check_parameters/` |Hier finden Sie die Regelsatzdefinitionen von allen mitgelieferten Check-Plugins von {CMK}. - -|`~/lib/check_mk/gui/plugins/wato/utils/pass:[__init__].py` |In dieser Datei sind die Gruppen der Setup-Oberfläche definiert, in welchen Sie neue Regelsätze ablegen können. -|`~/lib/check_mk/gui/plugins/metrics/` |Hier finden Sie die Metrikdefinitionen der mitgelieferten Plugins. -|`~/lib/check_mk/gui/plugins/metrics/unit.py` |In dieser Datei stehen die vordefinierten Einheiten für Metriken. -|`/usr/lib/check_mk_agent/plugins/` |Dieses Verzeichnis bezieht sich auf einen überwachten Linux-Host. Hier erwartet der {CMK}-Agent für Linux Erweiterungen des Agenten (Agentenplugins). -|=== - - - - - - - - - - - - - - - -// TK: Reste aus dem alten Artikel - -//// -[#outlook] -== Ausblick - -Es gibt noch viele weitere Aspekte und Themen rund um die Entwicklung von -eigenen Plugins. {CMK} hat sehr viele Schnittstellen für eigene Erweiterungen -und ist dadurch sehr flexibel erweiterbar. Wir arbeiten daran, dass diese -Schnittstellen nach und nach im Handbuch beschrieben werden. - -Falls Sie Fragen oder Schwierigkeiten haben, steht Ihnen natürlich unser professioneller -Support und auch das kostenlose Forum zur Verfügung. -//// - - -//// -In diesem Artikel werden in Zukunft noch weitere Aspekte der Plugin-Eentwicklung besprochen werden. -Die wichtigsten sind: - -== Checks im Cluster -== Checks mit einem Regelsatz, der die Discovery steuert -== Counter und andere persistierte Daten -// siehe Sphinx doc, 'get_rate', 'get_average'. -== Host- und Service-Labels erzeugen -== Includefunktionen, geteilter Code -== Man Pages schreiben -== Deklaratoren in den Sektionen, z.B. sep(...) -// wichtig: global VS. lokal -== Mehrere Checks für die gleiche Sektion -== Ein Check, der mehrere Sektionen auswertet - -= Ausblick - -Wenn das klappt, sind Sie eigentlich fertig. -Sie können das Ganze aber noch um etliche Zusatzfeatures erweitern, wie zum Beispiel: - -* Definitionen für die von den Services gelieferten xref:graphing#[Messdaten], damit schöne und gut beschriftete Graphen und „Perf-O-Meter“ erzeugt werden. -* Ein Regelsatz, mit dem Sie die Parameter des Check-Plugins konfigurieren können. -* Ein Regelsatz, welcher das Agentenplugin für die xref:wato_monitoringagents#bakery[Agentenbäckerei] konfiguriert. -* Ein Regelsatz, mit der der Spezialagent konfiguriert werden kann. -* Eine Manual Page (Man Page), welche das Check-Plugin für den Anwender dokumentiert. -* Ein xref:mkps#[MKP-Paket], in welchem das Plugin paketiert und einfach installierbar ist. - -Artikel dazu folgen hier in Kürze... -////