1. Einleitung
Die HW/SW-Inventur hilft, den Überblick über vorhandene Hardware zu behalten und jederzeit Kontrolle über installierte Software zu haben. Checkmk liefert fertige Plugins für viele Basis-Anwendungsfälle mit. Allerdings werden Sie in vielen Fällen bei spezieller Hard- und Software detailliertere Informationen ermitteln wollen. An dieser Stelle kommen eigene Plugins für die HW/SW-Inventur ins Spiel.
Für diesen Artikel setzen wir voraus, dass Sie bereits Grundkenntnisse in der Programmierung von agentenbasierten Check-Plugins haben. Wo sich die Verwendung der Inventur-API nicht wesentlich von der Verwendung der Check-API unterscheidet, fallen die Erklärungen in diesem Artikel daher etwas spärlicher aus. Dafür weisen wir an Stellen mit signifikanten Unterschieden umso deutlicher auf diese hin.
Bitte berücksichtigen Sie die Differenzierung zwischen Check- und Inventur-Plugin. Check-Plugins eignen sich vorrangig für sich schnell ändernde Daten, Inventur-Plugins für selten geänderte Daten. In diesem Artikel verwenden wir zur Veranschaulichung ein übersichtlich gehaltenes Beispiel mit USB-Geräten. Wollen Sie beispielsweise sicherstellen, dass niemand an irgendeinen Rechner im Monitoring USB-Geräte anschließt, die nicht auf einer Positivliste stehen, ist dafür ein Check-Plugin geeigneter. |
1.1. Die Check-API-Dokumentation
Die Inventur-API ist Teil der Check-API. Für den Zugriff auf die Dokumentation gilt dasselbe wie bei agentenbasierten Check-Plugins.
Sie können auf die mit Ihrer Checkmk-Instanz mitgelieferte Dokumentation zugreifen. Dazu navigieren Sie in der Checkmk-GUI zu Help > Developer resources > Plug-in API references. Wählen Sie im neuen Browserfenster in der linken Navigationsleiste Agent based ("Check API") > Version 2 aus. Auch ohne laufende Checkmk-Instanz können Sie sich die Plugin-API-Dokumentation als Kopie auf docs.checkmk.com/plugin-api ansehen.
2. Vorbereitung
Die Standardeinstellung von Checkmk sieht vor, Inventardaten nur einmal täglich zu aktualisieren. Dies steht natürlich im Widerspruch zu einer schnellen Sichtbarkeit von Ergebnissen bei eigenen Programmierversuchen. Daher raten wir zu zwei vorbereitenden Maßnahmen für mehr Geschwindigkeit ohne signifikante Systemlast:
Verzichten Sie bei dem Host, der die Daten für das neu zu erstellende Plugin liefert, auf das viel Last verursachende Plugin
mk_inventory.Setzen Sie für diesen Host das Ausführungsintervall des aktiven Checks auf eine oder wenige Minuten.
3. Das Agentenplugin
Wenn Sie ein agentenbasiertes Check-Plugin um Inventur-Funktionalität erweitern wollen, können Sie möglicherweise eine für Ihr Check-Plugin geschriebene Agentensektion mitverwenden.
Für unser Beispiel erstellen Sie eine eigene.
Die Grundlage hierfür ist der Befehl lsusb.
Ohne Parameter aufgerufen, gibt er eine Liste aller angeschlossenen USB-Geräte aus.
Das könnte so aussehen:
Die hier verwendete Kurzausgabe zeigt in den ersten Feldern, an welchem Bus und Port ein Gerät angeschlossen ist. Es folgt – durch einen Doppelpunkt getrennt – zuerst die Hersteller-ID, dann die Geräte-ID. Der Rest der Zeile enthält beschreibenden Text.
3.1. Das resultierende Skript
Da Checkmk mit zeilenweisen Ausgaben gut klarkommt, genügen für das benötigte Check-Plugin drei Zeilen:
Vergessen Sie nicht, das Plugin als ausführbar zu markieren:
Falls der Host, auf dem Sie das Agentenplugin testen, eine virtuelle Maschine ist oder Sie nicht ständig Geräte entfernen und anschließen wollen, speichern Sie das folgende Beispiel einfach in einer Datei im Spool-Verzeichnis. Spool-Dateien werden in die Agentenausgabe aufgenommen und mit übertragen. Achten Sie daher darauf, dass die Datei mit einem Zeilenumbruch endet.
Das Anschließen und Entfernen von USB-Geräten können Sie dann simulieren, indem Sie der Textdatei Zeilen hinzufügen oder welche entfernen.
3.2. Agent ausprobieren
Testen Sie die Agentenausgabe lokal mit dem folgenden Befehl:
Sie sollten nun je nach Zahl angeschlossener USB-Geräte (oder Zahl der Zeilen in der Spool-Datei) die Ausgabe von lsusb und ein paar Zeilen der folgenden Agentensektion sehen.
Achten Sie auf den korrekten Zeilenumbruch am Ende der Agentensektion.
4. Das Inventur-Plugin
Inventur-Plugins leben in der regulären Verzeichnisstruktur neben den normalen Check-Plugins.
Sie können Inventur-Plugins entweder als separate Dateien ablegen, dann empfehlen wir, als Präfix inventory_ zu verwenden.
Oder Sie integrieren Inventur-Plugins in gemeinsamen Dateien mit Check-Plugins, dann verzichten Sie auf das Präfix ganz.
Für welche Methode Sie sich entscheiden, sollten Sie von der Übersichtlichkeit und dem Bedarf nach gemeinsam verwendeten Funktionen abhängig machen.
Um ein reines Inventur-Plugin zu erstellen, müssen Sie daher ein passendes Verzeichnis anlegen:
4.1. Ein minimales Plugin
Sie können nun ein minimales Inventur-Plugin erstellen, welches Sie mit einem beliebigen Text-Editor bearbeiten:
Der Aufbau ähnelt dem von Check-Plugins.
Auch das Inventur-Plugin importiert aus dem Namensraum cmk.agent_based.v2.
Ein instanziiertes CheckPlugin-Objekt wird mit einer konkreten Check-Funktion verknüpft, die ein oder mehrere Result-Objekte via yield zurückgibt.
Analog dazu wird ein instanziiertes InventoryPlugin-Objekt mit einer konkreten Inventory-Funktion verknüpft, die ein Attributes-Objekt via yield zurückgibt.
Die Attributes, welche per yield zurückgegeben werden, implementieren das minimal sinnvolle Blatt eines Inventurbaumes:
Der Pfad (
path) beschreibt den relevanten Knoten in der Hierarchie des Baums, hier Hardware > Usb > General.Die Schlüssel-Wert-Paare im Objekt
inventory_attributeswerden als Schlüssel-Wert-Tabelle dargestellt.
Testen Sie zunächst, ob das Plugin syntaktisch korrekt ist und bei der Inventur gestartet werden kann:
Ist das der Fall, aktualisieren Sie die Konfiguration für den Monitoring-Kern und starten dann Ihre Instanz neu:
Ein Blick in das Inventar des Test-Hosts zeigt nun — sobald der nächste reguläre Check erfolgt ist — ein neues Blatt im Inventarbaum.
4.2. Die Parse-Funktion schreiben
Agentensektionen ohne Angabe eines Separators werden vor der Übergabe an die Inventurfunktion anhand des Default-Separators (Leerzeichen) in eine zweidimensionale Liste von Tokens umgewandelt: Je Zeile entsteht eine Liste von Tokens. Jede dieser Listen wiederum ist Element einer Liste von Zeilen. Wird ein Separator angegeben, definiert dieser die Grenze zwischen Tokens. Das erleichtert beispielsweise die Verarbeitung von als CSV formatierten Daten.
Eine Parse-Funktion wie bei agentenbasierten Check-Plugins ist nicht zwingend erforderlich. Dennoch kann die Verwendung einer separaten Parse-Funktion einige Vorteile haben. So erhöht sie Übersichtlichkeit und Erweiterbarkeit. Und wenn ein bereits vorhandenes Plugin eines agentenbasierten Checks weiterverwendet werden soll, stellt sich die Frage ohnehin nicht: Dann verwenden Sie einfach die vorhandene Parse-Funktion weiter.
In unserem Beispiel interessiert nicht, wo ein USB-Gerät angeschlossen ist. Für das Plugin sollen die Hersteller- und Geräte-ID ermittelt werden (an sechster Stelle der Liste), sowie die Beschreibung (ab der siebten Stelle bis zum Ende der Liste).
Während die maximal unterstützte USB-Version oben im simplen Beispiel noch einfach hart kodiert wurde, können Sie sich gerne Gedanken machen, wie Sie diese mittelfristig mit regulären Ausdrücken aus der Beschreibung ermitteln. Dafür wird die Beschreibung komplett benötigt, also alles ab dem siebten, bis zum letzten Wort der Liste:
4.3. Die Inventurfunktion schreiben
Hier können Sie ein wenig abkürzen: Im Gegensatz zu agentenbasierten Check-Plugins wird die Parse-Funktion nicht über die Erzeugung eines AgentSection-Objektes eingebunden, sondern muss aus der Inventurfunktion selbst aufgerufen werden.
Das Prinzip des yield zur fortwährenden Übergabe von Elementen kennen Sie aus agentenbasierten Check-Plugins:
Hier wird pro Zeile der Ausgabe von lsusb ein Objekt des Typs TableRow zurückgegeben.
Dieses enthält drei Spalten: Vendor- und Device-ID dienen gemeinsam als Schlüssel.
Die dritte, rein informative Spalte enthält die Beschreibung.
Schlüsselspalten haben die Besonderheit, dass sie zur Erkennung von Änderungen herangezogen werden, die wiederum im Monitoring zu einem Statuswechsel des Services HW/SW Inventory führen können. Des weiteren müssen Schlüssel eindeutig sein. Weitere Zeilen mit derselben Kombination aus Vendor- und Device-ID werden folglich ignoriert.
Neben Schlüssel- und Inventarspalten können Sie die Einträge der Tabelle zusätzlich mit Statusspalten (status_columns) und analog Statusattributen (status_attributes) beschreiben, welche den Zustand eines Objektes abbilden.
Für den Wert des Zustands stehen die einfachen Datentypen (int, float, str, bool oder None) zur Verfügung.
Wollen Sie beispielsweise eine Tabelle erstellen, in der verzeichnet ist, welcher Anschluss (Bus/Device) belegt ist, dann können Sie key_columns (Bus und Device) und status_columns (hier: bool) verwenden.
4.4. Das fertige Plugin
Das fertige Plugin fügt alles zusammen.
Hier wird zusätzlich noch die TableRow-Klasse importiert:
5. Regelsätze für die Inventur
Da die Inventur-API Teil der Check-API ist, gestaltet sich die Erstellung und Anwendung von Regelsätzen exakt wie bei der Entwicklung regulärer Check-Plugins. Aus diesem Grund zeigen wir hier nur ein minimales Beispiel.
5.1. Regelsatz definieren
In der Ausgabe von lsusb tauchen einige Zeilen für Hubs (externe und Root-Hubs) auf.
Diese Geräte ohne eigene Funktionalität erschweren es, den Überblick zu behalten.
Daher sollen die Benutzer die Möglichkeit haben, selbst zu entscheiden, ob Hubs erfasst werden sollen oder nicht. Die Einstellung sollen sie über eine Checkbox in einer Regel vornehmen können.
Erstellen Sie hierfür den Ordner rulesets der Plugin-Familie:
In diesem Ordner legen Sie nun den Regelsatz an:
Wenn Sie das Skript für den Regelsatz abgespeichert haben, validieren Sie es mit cmk-validate-plugins und starten Sie dann die Instanz mit omd restart neu.
Dass cmk-validate-plugins vorhandene, aber nicht verknüpfte Regelsätze bemängelt, dürfen Sie an dieser Stelle ignorieren.
Die Verknüpfung erfolgt im nächsten Abschnitt.
Sie können die Regel jetzt in der GUI unter Setup > Hosts > HW/SW inventory rules einsehen und anpassen. Ist der Name einer Regel bekannt (hier: Lsusb inventory demo), können Sie diesen direkt ins Suchfeld tippen. Mit Add rule können Sie die Regel, die Sie soeben vorbereitet haben, anlegen und wie gewohnt auf Ordner oder Hosts mit von Ihnen gewählten Eigenschaften anwenden.
5.2. Regelsatz anwenden
Noch ist der Regelsatz nicht verknüpft.
Um ihn anzuwenden, ergänzen Sie zwei weitere Named Arguments bei der Erstellung der InventoryPlugin-Klasse.
Zusätzlich muss der Inventurfunktion als erstes Argument das Parameter-Dictionary übergeben werden.
Hier ergänzen Sie auch einen regulären Ausdruck, um zu erkennen, welche Gerät-Beschreibungen mit „hub“ enden.
Beachten Sie, dass Sie dafür eine weitere import-Zeile zu Beginn des Skripts einfügen müssen:
6. Erweiterungsmöglichkeiten
6.1. Mehrere Sektionen abonnieren
Im Beispiel haben Sie sich bisher an das Standardverhalten gehalten: Ein Inventur-Plugin abonniert eine Agentensektion, welche denselben Namen trägt, wie das Inventur-Plugin. Aber was tun, wenn Sie vorhandene Agentensektionen mitverwenden wollen, die bereits von einem vorhandenen Inventur-Plugin ausgewertet werden? Hier ist die Wahrscheinlichkeit hoch, dass bereits ein Inventur-Plugin existiert, das eben diesen Namen der Agentensektion verwendet. So verschaffen Sie sich einen Überblick über bereits existierende Sektionen:
In diesem Fall und dem Fall, dass ein Inventur-Plugin mehrere Agentensektionen auswerten soll, können Sie die abonnierten Sektionen als Liste angeben. Gehen wir beispielsweise davon aus, dass Sie zwei Agentensektionen auswerten wollen, die sich auf fiktionale Dinge beziehen:
In diesem Fall müssen die abonnierten Agentensektionen explizit angegeben werden. Der Name des Plugins muss mit den Agentensektionen nicht mehr übereinstimmen:
Die Inventurfunktion bekommt dann die String-Tabellen als Variablen nach dem Muster section_sectionname übergeben:
Das vollständige Plugin, welches die Versionen von Yoyodyne Gnomovision und Zorg ZF1 im Inventarbaum abbildet, kann dann so aussehen:
