Icinga 2 Best Practice Teil 1: Zonen und Agenten

This entry is part 1 of 7 in the series Icinga 2 Best Practice

Heute bin ich endlich mal wieder dran einen Blogpost zu schreiben und wie jedes Mal, kann ich mich nicht entscheiden über welches Thema ich berichten möchte. Eigentlich sollte hier nach meinem Plan ein Post über den Rewrite des Icinga 2 Moduls für Puppet stehen. Aber wie schon häufiger habe ich mich in nahezu letzter Sekunde umentschieden und möchte heute mit einer Reihe starten, in der es um die Organisation von Icinga 2 Host- und Service-Objekten gehen wird. Den Start macht der erste Teil zur Konfiguration des Icinga 2 Agenten.
Im Icinga-Kosmos gibt es im Grunde zwei unterschiedliche Arten von Plugins. Bei der Ersten handelt es sich um Plugins, die ihre Werte über eine Netzwerkverbindung ermitteln z.B. check_http. Die zweite Art sind dann solche, die ihre Werte lokal auf dem zu überwachenden Host ermitteln müssen wie check_disk oder check_load. Mit dem Agenten ist uns seit geraumer Zeit eine Möglichkeit in die Hand gegeben mit der diese letzteren Plugins lokal ausgeführt werden können. Hierbei wird vom eigentlichen Icinga 2 Server eine persistente, verschlüsselte und an Hand von Zertifikaten authentifizierte Verbindung zum zu überwachenden Host aufgebaut. Über diese stößt dann der Server auf dem Agenten die Ausführung des gewünschten Plugins an.
Die eigentlich Installation, z.B. mit dem node wizard ist hier bitte der Online-Dokumentation zu entnehmen. Bzgl. der Konfiguration ist am Agenten zu hinterlegen wer hier überhaupt mit einander kommuniziert.

# cat /etc/icinga2/zones.conf
object Endpoint "agent.example.org" {
}
object Endpoint "server.example.org" {
  host = "x.x.x.x"
}
object Zone "agent.example.org" {
  endpoints = ["agent.example.org"]
  parent = "master"
}
object Zone "master" {
  endpoints = ["server.example.org"]
}

Jedes Endpoint-Objekt ist eine laufende Icinga-Instanz. Einer Zone muss immer mindestens ein Endpoint zugeordnet sein. Daten werden zwischen Zonen ausgetauscht. Hier sorgt das parent-Attribut, dass Checkresults an die Master-Zone und damit an deren Endpoints geliefert werden. Für das Objekt server.example.org vom Type Endpoint ist das Attribut host mit der zugehörigen IP-Adresse gesetzt, damit ist konfiguriert, dass der Agent die Verbindung zum Server aufbaut.
Auch der andere Kommunikationspartner in der master-Zone muss wissen von wem er Daten empfangen und an wen er selbst welche senden darf. Dort schreiben wir jedoch lediglich die Information zur eigenen Seite in die Datei zones.conf.

# cat /etc/icinga2/zones.conf
object Endpoint "server.example.org" {
}
object Zone "master" {
  endpoints = ["server.example.org"]
}

Das agentenzugehörige Endpoint- und Zonen-Objekt kommt mit dem Host-Objekt zusammen in eine “normale” Konfigurationsdatei für zu überwachende Objekte, z.B. unterhalb von /etc/icinga2/conf.d.

object Endpoint "agent.example.org" {
}
object Zone "agent.example.org" {
  endpoints = ["agent.example.org"]
  parent = "master"
}
object Host "agent.example.org" {
  import "generic-host"
  display_name = "agent"
  ...
}

Dies erleichtert einem in zweierlei Hinsicht die Konfiguration:

1) Es stehen alle Informationen an einer Stelle und können beim Wegfall des Hosts auch dort zusammen entfernt werden ohne noch zusätzlich an anderen Orten suchen zu müssen.
2) Betreibt man Satelliten, wird so das Endpoint- und Zonen-Objekt via Synchronisation der Konfiguration automatisch auf den entsprechenden Satelliten verteilt bzw. entfernt.

Auf dieser Seite verzichte ich auf die Angabe einer IP mittels host, d.h. es erfolgt kein Versuch eines Verbindungsaufbaus zum Agenten. Der Server warte also darauf das der Agent von sich aus die Kommunikation initiiert. Zu überwachende Hosts werden auch mal gewartet und sind dann nicht erreichbar. So erfolgen keine überflüssigen Verbindungsversuche und schreiben das Logfile voll. Ein weiterer Tipp ist, die Namen für Endpoint- und Host-Objekt identisch zu wählen. Damit sieht eine Servicedefinition z.B. für load wie folgt aus:

apply Service "load" {
  import "generic-service"
  check_command = "load"
  command_endpoint = host.name
  assign where host.vars.os == "Linux"
}

Da check_load auf einem Endpoint (Agenten) auszuführen ist, wird mit dem Service-Attribut command_endpoint festgelegt auf welchem dies zu erfolgen hat. Ein Service ist immer einem Host zugeordnet, so kann aus dem Service heraus auf Attribute des zugehörigen Hosts zugegriffen werden. Da für command_endpoint ein existierender Endpoint anzugeben ist, wir diesem per Konvention den selben Namen wie dem Host-Objekt gaben, kann hier nun host.name benutzt werden.

TIPP: Kommen keine Ergebnisse vom Agenten auf dem Icinga-Server (Master) an, fehlt auf Seite des Agenten die Angabe der parent-Zone. Wird keine Konfiguration zum Agenten synchronisiert, fehlt die parent-Zone auf der Seite des Servers.

Zum Thema synchronisieren von Konfigurationsdateien und globalen Zonen, dann in Teil 2.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 2: Konfiguration synchronisieren

This entry is part 2 of 7 in the series Icinga 2 Best Practice

Im heutigen Teil 2 dieser Serie, befassen wir uns mit Konfiguration und deren Synchronisation zwischen Icinga 2 Instanzen in verteilten Umgebungen. Hierbei ist eine solche verteilte Umgebung schon gegeben, wenn der Agent zum Einsatz kommt, also immer wenn Endpoints und Zones beteiligt sind. In Teil 1 wurde Tipps zur Konfiguration der Verbindungsdaten gegeben, heute vertiefen wir, was auf dem Agenten passiert und was dort zusätzlich zum Icinga 2 Core benötigt wird.
Zuerst werden die jeweiligen Plugins auf den Agenten benötigt, seien es für Linux die Monitoring-Plugins oder auf der Windows-Seite die nativen aus dem Icinga-Projekt oder die Plugins vom NSClient++. Bei Icinga, auch in der neuen Version 2, ist das Objekt vom Typ CheckCommand das Bindeglied zwischen Host- bzw. Service-Objekt und dem konfigurierten Plugin, was zur Statusermittlung aufzurufen ist. Wobei ein lokal auszuführender Host-Check üblicherweise nicht Praxisrelevant ist. Das CheckCommand beschreibt wie das Plugin aufzurufen ist, den Ort im Dateisystem wie auch mit welchen Optionen das Plugin ausgeführt wird.

object CheckCommand "mem" {
  command = [ PluginContribDir + "/check_mem.pl" ]
  arguments = {
    "-u" = {
      set_if = "$mem_used$"
      description = "Check USED memory"
    }
...

Für die Pfadangabe zum Plugin ist immer die Verwendung einer Konstanten zu empfehlen, da sich der Ort von Plattform zu Plattform unterscheiden kann und eine Konstante ist für jede Instanz und damit für jeden Agenten gesondert setzbar. Standardmäßig sollten Konstanten in constants.conf im Icinga-Konfigurationsverzeichnis definiert werden.
Beim Plugin check_mem.pl im obigen Beispiel handelt es sich nicht um eines vom Monitoring-Plugin-Projekt, sondern um eines was gesondert auf den Agenten zu installieren ist. Ausserdem benötigen die Icinga-Instanzen der Agenten obige Definition. Für das CheckCommand mem wird diese schon in der ITL (Icinga Template Library) mit geliefert und muss nicht selbst erstellt werden. Auf den jeweiligen Agenten ist lediglich die Definition in icinga2.conf als include einzubinden. Sie befindet sich im PluginContrib-Bereich der ITL.

include <plugins>
include <plugins-contrib>

In plugins hingegen befinden sich die Definitionen zu Plugins aus dem Monitoring-Projekt. Bei Windows empfiehlt sich die Definitionen der nativen (windows-plugins) bzw. NSClient++-Plugins (nscp) einzubinden.
Was aber nun, wenn man nun für ein Plugin selbst ein CheckCommand-Objekt anlegen muss? Es jeweils auf allen Agenten in die ITL legen? Nein, der ITL-Bereich ist ausschließlich für vom Projekt gepflegte Objekte, eigene würden beim nächsten Update wieder verschwinden. Ausserdem ist eine zusätzliche Pflege von sich ändernden Konfigurationsdateien für jeden Agenten nicht erstrebenswert. Hier kommen nun globale Zonen ins Spiel, die einem Konfiguration auf beliebige Endpoints synchronisieren und automatisch laden.

object Zone "linux-commands" {
  global = true
}
object Zone "windows-commands" {
  global = true
}

Im Gegensatz zum Server bzw. Master und Satelliten-Systemen, ist auf den Windows- und Linux-Agenten nur die entsprechende Zone in der zones.conf einzutragen. In beiden werden auch wirklich nur CheckCommands hinterlegt, Services und Templates werden in der Regel nicht auf Agenten benötigt und sind damit Informationen die besser nicht leicht zugänglich sowie zentral gehalten werden.
Windows kennt leider keinen graceful restart bzw. reload wie Unixsysteme und somit ist hier die Trennung sinnvoll, da Windows einen Stop des Dienstes und nachgelagertem Start nur ausführen muss, wenn es wirklich nötig ist.
Teil 3 dieser Reihe wird sich dann mit praktischen Definition von Services beschäftigen und wie diese ausschließlich aus den zugehörigen Host-Objekten parametresiert werden.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 3: Services überwachen

This entry is part 3 of 7 in the series Icinga 2 Best Practice

Nun in Teil 3 dieser Serie werden wir uns näher damit beschäftigen wie in Icinga 2 Services überwacht werden bzw. wie es zu konfigurieren ist, dass bestimmte Services nur auf bestimmten Hosts überwacht werden. Hier bietet Icinga 2 als Neuerung eine regelbasierte Zuweisung an Host-Objekte die definierten Eigenschaften genügen.

apply Service "ping4" {
  import "generic-service"
  check_command = "ping"
  assign where host.address || host.address6
}

So wird hier ein Service ping4 an alle Hosts “gebunden”, die das Attribut address oder address6 definiert haben. Nach diesem recht einfachem Beispiel wenden wir uns auch gleich etwas komplizierterem zu, der Überwachung von Dateisystemen auf einem Linux-System.

apply Service for (filesystem => config in host.vars.disks) {
  import "generic-service"
  check_command = "disk"
  command_endpoint = host.name
  vars += config
  assign where host.vars.os == "Linux"
  ignore where typeof(config) != Dictionary
}

Da hier über das CheckCommand disk, das Plugin check_disk zur Anwendung gelangt, das lokal auf dem zu überwachenden System laufen muss, wird es via command_endpoint auf genau diesem Endpoint angetriggert (siehe hierzu Teil 1 dieser Serie). Ein Host kann mehrere unterschiedlich Dateisysteme beherbergen, deshalb sind diese im Host-Objekt mit dem Custom-Attribute vars.disks zu definieren. Ausserdem muss zusätzlich, wie in dem assign-Statement gefordert, vars.os auf Linux gesetzt sein.

object Host "host.example.org" {
  ...
  vars.os = "Linux"
  vars.disks["disk /"] = {
    disk_partition = "/"
  }
  vars.disks|"disk /tmp"] = {
    disk_partition = "/tmp"
  }
}

Bekanntlich handelt es sich bei vars um ein Dictionary und vars.disks ist eine Darstellungsform eines Keys in diesem Dictionary. Eine andere Form einen Schlüssel anzusprechen ist der Index mit []-Klammern, wie in vars.disks[“disk /”]. Das heißt wir haben hier ein Dictionary in einem Dictionary. Und um es noch auf die Spitze zu treiben weisen wir den einzelnen Keys als Wert wieder jeweils ein Dictionary zu, Perl lässt grüßen. Wozu nun das Ganze? Mit apply Service for wird der Inhalt von vars.disks durchlaufen. Da es sich hierbei um ein Dictionary handelt, wird hier ein for-each verwendet, zu sehen an filesystem => config. Beides sind hier unsere Laufvariablen für die Schleife. Der Variablen filesystem wird jeweils der Key zu gewiesen, also beim ersten Durchlauf “disk /” und beim Zweiten “disk /tmp”, in config dann demnach der zugehörige Wert. Diesen Wert, selbst ein Dictionary, kann als Konfigurations-Dictionary bezeichnet werden, der den jeweiligen Pluginaufruf von disk parametrisiert. Die möglichen Parameter für disk sind sehr gut der Online-Dokumentation zu entnehmen. Wie dies funktioniert und warum die Zeile vars += config hierzu eine zentrale Rolle spielt, wird in Teil 4 erklärt werden. Der Name des Services entspricht standardmäßig dem Inhalt von filesystem.
Selbstverständlich besitzt jedes Linux-System ein Root-Dateisystem und sagen wir, bei uns auch ein eigenes für /tmp. Natürlich möchte man nun nicht für alle seine Hosts immer diese obigen 7 Zeilen angeben müssen, deshalb definieren wir mit diesen ein Host-Template mit der Bezeichnung linux-host. Nun haben Regeln die dumme Eigenheit ihre Ausnahmen zu haben, z.B. hat der Host host.example.org im Gegensatz zu allen anderen Hosts eben kein Dateisystem /tmp. Was dann?

object Host "host.example.org" {
  import "generic-host"
  vars.disks["disk /tmp"] = false
}

Hier wird nun für /tmp die Definition aus dem Template nachträglich überschrieben. Das ignore-Statement in unserer Service-Definition sorgt dafür, dass alle Dateisysteme, denen kein Konfiguration-Dictionary zugewiesen ist, auch nicht als Service in unserer Icinga-Konfiguration landen. Bei typeof handelt es sich um eine Funktion, die die Typesierung einer Variablen ermittelt.
Bis zum nächsten Mal, ihr müsst unbedingt schauen wie es weiter geht. In Teil 4 folgt die etwas theoretische Erklärung wie solche Services jeweils einzeln unterschiedlich parametrisiert werden.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 4: Host Templates und Services

This entry is part 4 of 7 in the series Icinga 2 Best Practice

Heute soll es um die Strukturierung von Services und deren Zuordnungen zu Gruppen von Hosts gehen. Ein Host Template kann zur Zusammenfassung von Informationen einer Gruppe von Hosts dienen und damit mehrere unterschiedliche Services für den jeweiligen Host anziehen. Wir wollen in den folgenden Beispielen Linux Hosts überwachen. Dort neben, wie schon in Teil 3 beschrieben, der Belegung der Dateisysteme auch in Abhängigkeit in welchem Netzsegment der Host angeschlossen ist, ob die Zeit synchron zum Zeitserver läuft.

template Host "linux-host" {
  import "generic-host"
  vars.os = "Linux"
  vars.disks["disk /"] = {
    disk_partition = "/"
  }
}
apply Service "time" {
  import "generic-service"
  check_command = "ntp_time"
  command_endpoint = host_name
  assign where host.vars.os == "Linux"
}

Es gibt zwei Netze mit je eigenem Zeitserver. Um dieses abzubilden, definieren wir für jedes Netz ein eigenes Host-Template:

template Host "dmz-net" {
  vars.ntp_address = "172.16.2.99"
}
template Host "lan-net" {
  vars.ntp_address = "172.16.1.99"
}

Diese beiden Templates enthalten nur netzspezifische Informationen, in unserem Beispiel auch nur den jeweilig zuständigen Zeitserver. Der Service-Check time mit dem Plugin check_ntp_time ermittelt die Differenz zwischen der lokalen Zeit des Hosts und der Zeit des NTP-Servers, der in ntp_address angegeben ist. Nun müssen wir für einen Host im internen Netzwerk lan-net nur noch beide Templates zusammen bringen:

object Host "host.example.org" {
  import "linux-host"
  import "lan-net"
  import "postgres-dbms"
  address = "172.16.1.11"
}

Habe wir weitere Services, die abhängig vom Netzsegment unterschiedlich zu konfigurieren sind, können diese Informationen den Netz-Templates hinzugefügt werden. Ein weiteres Beispiel wäre hier die Überwachung unterschiedlicher Domain Name Services. Diese Konzept der Stapelung von Host templates kann natürlich noch weitergeführt werden, z.B. auf Applikationen wie einen Postgresql basierendes Datenbank-Management-Systems bezogen. Ggf. muss jedoch auf die Reihenfolge der Importe geachtet werden, wenn Werte überschrieben werden sollen.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 5: Autosign von Zertifikatsanfragen in verteilten Umgebungen

This entry is part 5 of 7 in the series Icinga 2 Best Practice

Ein jeder kennt das Problem, im Unternehmensnetz gibt es unterschiedliche netzwerkbezogene Sicherheitszonen, die mittels Perimeter voneinander getrennt sind. Für das Monitoring bedeutet dies im Idealfall, man stellt in jeder dieser Zonen einen Icinga-Satelliten bereit, der die von ihm ermittelten Ergebnisse an eine zentrale Instanz weiter meldet, den Icinga-Master. Damit ist gewährleistet, was Firewall-Admins berechtigterweise verlangen, lediglich Punkt-zu-Punkt-Verbindungen zu erlauben. Auch die Richtung des Verbindungsaufbaus ist mit Icinga 2 wählbar.
Setzt man zusätzlich auch Icinga 2 in der Ausprägung Agent ein, benötigt dieser ein signiertes Zertifikat um mit seinem jeweiligen Satelliten oder direkt mit dem Master zu kommunizieren. Im letzten Fall entstehen hieraus keine Probleme. In der Regel wird die CA, in einer Umgebung mit mehreren Satelliten auf dem Master betrieben. Bei lediglich einem Satelliten könnte die CA auch auf genau diesem Satelliten laufen, was jedoch Sicherheitsbedenken hervorruft. Ein Zertifikat aus einer niedrigen Sicherheitszone könnte verwendet werden, um mit einer höheren zu kommunizieren. Gleiches gilt natürlich auch bei der Benutzung lediglich einer CA, aber die Netzwerksicherheit soll ja dieses Risiko Minimieren.
Bleibt das Problem auf einem neuinstallierten Agenten mittels Autosigning ein beglaubigtes Zertifikat zu erhalten. Hier kann eine eigene CA auf jedem Satelliten Abhilfe schaffen. Der jeweilige Agent benötigt nun nur eine Verbindung zu seinem Satelliten um einen Request zu senden und keine Kommunikation zum Master. Wie wird nun dieses genau bewerkstelligt?

  • Erstellen einer CA auf dem Satelliten
  • Der Satellit bekommt ein Zertifikat signiert von seiner eigene CA
  • Der Satellit benötigt sein eigenes RootCA-Zertifikat und das vom Master
  • Der Master bekommt umgekehrt ebenfalls das RootCA vom Satelliten
Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 6: Service-Checks auf Agenten schedulen

This entry is part 6 of 7 in the series Icinga 2 Best Practice

In Teil 1 dieser Blogserien beschäftigten wir uns mit Zonen, Agenten und wie man zentral angetriggert Plugins auf diesen Agenten ausführt. Nun im aktuellen Teil soll es darum gehen, das einplanen und ausführen dem Agenten selbst zu überlassen, damit werden auch während eines Verbindungsausfalls weiter Daten gesammelt und später, wenn die Verbindung wieder besteht, nachträglich übertragen.
In dem hier folgenden verdeutlichen wir, wie dies zu konfigurieren ist, mit einem Beispiel von einer Zone master, einer globalen Zone global-templates und einer Zone zu einem Beispiel Agenten agent.example.org.
Die Zonen- und Endpoint-Definitionen des Agenten auf Seite des Masters müssen in einer Datei in der Master-Zone oder in zones.conf hinterlegt werden. Leider ist es hier nicht möglich die jeweiligen Host-, Zonen- und Endpoint-Objekte in einer Datei zusammen zufassen, da wir das Host-Objekt agent.example.org zum Agenten selbst synchronisieren müssen und diese Definition in der Agenten-Zone abgelegt sein muss, z.B. in der Datei zones.d/agent.example.org/agent.conf:

object Host "agent.example.org“ {
 import "generic-host"
 address = "10.0.10.42"
 zone = get_object(Zone, name).parent
 vars.os = "Linux“
}

Durch die Ablage an genau diesem Ort, wird das Objekt zum Agenten synchronisiert. Mit setzen des Attributes zone auf die eigene Parent-Zone sorgen wir jedoch wieder dafür, dass das Host-CheckCommand sowie alle an diesen Host gebundenen Services auf einem Endpoint der Parent-Zone ausgeführt werden, hier die Zone master.
Damit wird der hostalive vom Master ausgeführt, was gewünscht ist, da sonst der Host seine Erreichbarkeit von sich selbst aus testen würde. D.h. alle Services, die auf den Agenten über das Netzwerk zugreifen sollen, bleiben wie sie sind und wir müssen keine Anpassungen vornehmen. Ganz im Gegensatz zu Services, die ein Plugin lokal auf dem Agenten ausführen sollen.

apply Service "load“ {
 import "generic-service“
 check_command = "load“
 if ( get_object(Zone, host.name) ) {
   zone = host.name
 }
 assign where host.vars.os == "Linux"
}

Auch Objekte vom Typ Service besitzen das Attribut zone mit dem wir hier nun die umgekehrten Weg beschreiten, existiert zum Host eine Zone selben Namens, wird die Zone zur Ausführung des zugeordneten Plugins in die eigene Agenten-Zone verlegt.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 7: "Friss oder stirb" der Variablen-Scope

This entry is part 7 of 7 in the series Icinga 2 Best Practice

Aus der Dokumentation kann man entnehmen das Objekte und Funktionen ihren eigene Variablen-Scope besitzen und nicht ohne weiteres auf Variablen eines “übergelagerten Scopes” zu greifen dürfen. Sollen z.B. Objekte über eine Schleife erzeugt werden, die sich nur maginal voneinander unterscheiden, können Variablen an diese bzw. dessen Scope via use gegeben werden.

template CheckCommand "by_ssh_base" {
  import "by_ssh"
  vars.by_ssh_plugindir = PluginDir
}
var cmdlist = [ "load", ""ntp_time", "yum" ]
for (cmd in cmdlist) {
  object CheckCommand "by_ssh_" + cmd use(cmd) {
    import cmd
    vars.by_ssh_arguments = arguments
    arguments = null
    vars.by_ssh_command = "$by_ssh_plugindir$/check_" + cmd
    import "by_ssh_base"
  }
}

Gleiches Verfahren lässt sich auch für das Erzeugen von Services über eine Schleife anwenden. Hier soll sich nun jedoch die Service-Bezeichnung vom verwendenten Check-Command unterscheiden und es kommt kein Array zum Einsatz, sondern ein Dictionary. Das folgende Beispiel ist allerdings für Commands, die via command_endpoint auf einer anderen Icinga-2-Instanz aufgerufen werden.

var srvlist = {
        "load" = "load"
        "time" = "ntp_time"
        "updates" = "yum"
}
for (srv => cmd in srvlist) {
  apply Service srv use(cmd) {
    import "generic-service"
    check_command = cmd
    command_endpoint = host.name
    assign where host.vars.os == "Linux"
    ignore where host.vars.noagent
  }
}

Werden im apply-Block mehrere Variablen, z.B. im obigen Beispiel auch srv, kann use durch Komma getrennt mehrere Variablen “weiter reichen”.

Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.