Einspielen von CheckCommands über die Director-API

 

Generell funktioniert das Einspielen von Konfiguration über den Director ja schon ganz ordentlich. Manchmal stoßen wir dabei jedoch auch an Grenzen, die oft dem aktuellen Entwicklungsstand des Projekts oder den unendlichen Möglichkeiten von icinga geschuldet sind.

icinga configuration is a full-blown DSL, not just a configuration format

 

Wir sind bei Version 1.3 angekommen und ich finde die Möglichkeiten schon beeindruckend. Nach wie vor lassen sich kompliziertere Konstrukte wie Conditionals, Loops etc. nicht oder nur spärlich realisieren, allerdings gibt es für fast alle diese Anwendungsfälle auch andere Lösungen/Workarounds die für die meisten unserer Kunden ausreichen und in diesem immer noch frühen Entwicklungsstadium völlig in Ordnung sind.

Somit nimmt der icinga Director in unserem produktiven Umfeld seine Rolle als Konfigurationshelfer bereits ausreichend wahr und man kann auch Kunden begeistern die mit der Icinga-Sprache nicht ganz so vertraut sind.

Heute möchten wir kurz zeigen, wie man sich trotz einiger (noch)-Schwächen des Directors (fehlende Unterstützung für sämtliche Sprachelemente aus icinga) helfen kann.

Wir wollen ein CheckCommand per Director-API einspielen. Warum?

Im Director gibt es aktuell keine Möglichkeit z.B. die Attribute skip_key oder description eines CheckCommands zu setzen. Nun könnte man diese Objekte klassisch unter conf.d ablegen, allerdings können diese dann nicht im Director verwendet werden. Außerdem möchten wir unseren Kunden gern eine angepasste Installation übergeben, sodass er sich selbst nur noch um seine individuelle Konfiguration kümmern muss.

Gleich vorweg: Über die API wird grundsätzlich alles was icinga an Möglichkeiten bietet auch unterstützt. Aufgrund des erhöhten Programmieraufwands werden allerdings nie alle Möglichkeiten auch so im Director abgebildet werden können.


API Helper Script

 

Die API kann man z.B. mit Curl bedienen. Freundlicherweise wird hier in der Doku schon ein passendes Script mitgeliefert:

#!/bin/bash
 
METHOD=$1
URL=$2
BODY="$3"
USERNAME="web2login"
PASSWORD="web2passwd"
 
test -z "$PASSWORD" || USERNAME="$USERNAME:$PASSWORD"
 
test -z "$BODY" && curl -u "$USERNAME" \
  -i http://localhost/icingaweb2/$URL \
  -H 'Accept: application/json' \
  -X $METHOD
 
test -z "$BODY" || curl -u "$USERNAME" \
  -i http://localhost/icingaweb2/$URL \
  -H 'Accept: application/json' \
  -X $METHOD \
  -d "$BODY"
 
echo

Es reicht, die Parameter USERNAME und PASSWORD anzupassen. Eventuell noch die URL zum API-Endpoint.

Wichtig: Hier wird nicht die icinga-API benutzt, sondern die Director-API, welche über die Icingaweb2-Oberfläche anzusprechen ist und deshalb auch die entsprechenden Login-Daten verlangt.

Das Script könnt ihr z.B unter /usr/local/bin/director-api speichern. Somit liegt es dann direkt im Pfad. Nun noch ausführbar machen mit chmod +x /usr/local/bin/director-api und fertig ist unser Helper Script.


Erstellen des CheckCommands

 

Als nächstes erstellen wir unser CheckCommand im JSON-Fomat (vim sansymphony_snmp). Dies legen wir gern als Datei im Ansible-Repository ab und rollen es auf den entsprechenden Maschinen aus.

{
    "arguments": {
    "-H": {
    "value": "$sansymphony_snmp_host_address$",
    "description": "Host address of the SNMP device, Can be a comma separated list, so the plugin will try to reach one of them. If one of the node answers, the results are displayed.",
    "required": true
    },
    "-t": {
    "value": "$sansymphony_snmp_timeout$",
    "description": "Timeout in seconds for a SNMP request. Default: 10"
    },
    "-c": {
    "value": "$sansymphony_snmp_community$",
    "required": true
    },
    "--cache": {
    "value": "$sansymphony_snmp_cache$",
    "description": "Cache SNMP data locally for usage by multiple services. The SNMP data is stored in /tmp/check_sansymphony_<address>. Value of this parameter is amount of minutes the cache is valid."
    },
    "-P": {
    "value": "$sansymphony_snmp_protocol$",
    "description": "SNMP protocol to use (either 1, 2 or 3)",
    "required": true
    },
    "-i": {
    "value": "$sansymphony_snmp_included_types$",
    "description": "Included monitor types (first column). List of comma separated strings, can be specified multiple times. Default: all"
    },
    "-e": {
    "value": "$sansymphony_snmp_excluded_types$",
    "description": "Excluded monitor types (first column). List of comma separated strings, can be specified multiple times. Default: none"
    }
    },
    "command": "CustomPluginDir + \/check_sansymphony",
    "methods_execute": "PluginCheck",
    "object_name": "sansymphony_snmp",
    "object_type": "object",
    "timeout": "60",
    "vars": {
    "sansymphony_snmp_host_address": "$address$",
    "sansymphony_snmp_community": "public",
    "sansymphony_snmp_type": "health",
    "sansymphony_snmp_protocol": 1,
    "sansymphony_snmp_cache": 5
    }
}

Wir rufen das API-Script derzeit vom jeweiligen Host aus auf da unsere Ansible-Struktur eher dezentral ist. Durch die Automatisierung passt das so für uns. Man kann sich natürlich auch eine zentrale Stelle für den API-Call vorstellen.

Je nachdem ob ihr ein Objekt neu anlegen oder verändern wollt, müssen unterschiedliche URL-Endpunkte angesprochen werden.

Anlegen:

director-api POST director/command @sansymphony_snmp

 

Anpassen:

director-api POST director/command?name=sansymphony_snmp @sansymphony_snmp

Rückmeldungen der API:

Bei erfolgreichem Anlegen des Objekts:

HTTP/1.1 201 Created
Date: Tue, 02 May 2017 07:34:12 GMT
Server: Apache/2.4.10 (Debian)
Content-Length: 3068
Content-Type: application/json

 

Bei keiner Änderung:

HTTP/1.1 304 Not Modified
Date: Tue, 01 Mar 2016 04:52:05 GMT
Server: Apache

Bei erfolgreicher Änderung:

HTTP/1.1 201 Created
Date: Tue, 02 May 2017 07:34:12 GMT
Server: Apache/2.4.10 (Debian)
Content-Length: 3068
Content-Type: application/json

 

Erneutes Anlegen eines schon vorhandenen Objekts schlägt fehl:

HTTP/1.1 500 Internal Server Error
Date: Tue, 02 May 2017 08:06:28 GMT
Server: Apache/2.4.10 (Debian)
Content-Length: 65
Connection: close
Content-Type: application/json
 
{
    "error": "Trying to recreate icinga_command (sansymphony_snmp)"
}

 

Was haben wir getan?

  • dem Director ein icinga Objekt bekannt gemacht (API)
  • icinga Sprachelemente genutzt die so im Director noch nicht zur Verfügung stehen (z.B default vars, description, skip_key) um kleine Schwächen des Directors zu umgehen
  • das Objekt im Director nutzbar gemacht
  • (noch) KEIN icinga Objekt eingespielt (dazu muss erst der Config Check einmal gelaufen sein)

 

Deploy der Änderungen

Wer den Director kennt, weiß, dass Änderungen niemals ohne weiteres Deployment aktiv sind. Wir müssen noch dafür sorgen, dass ein Config-Check durchgeführt und anschließend die Konfiguration erneut ausgerollt wird.

Das geht entweder über die Web-Oberfläche oder auch bequem über die Director-CLI:

icingacli director config deploy

 

Wir rollen seit einiger Zeit auf diese Weise unsere CheckCommands auf den Kundenmaschinen aus. Die CheckCommands sind universell einsetzbar und wiederverwendbar.

Damit ersparen wir uns lästige Klickerei und unseren Kunden Bares 😉

Ergänzung: Es gibt als Ergänzung zum Director-Modul für die Web2 Oberfläche auch noch andere grafische Helfer womit sich Sprachelemente aus icinga über den Director einspielen lassen bzw. sich sogar normale icinga-Config importieren lässt. Man verzichtet bei Anwendung solcher Methoden allerdings meist auf den Syntax-Check des Directors. [Hier](http://github.com/Icinga/icingaweb2-module-fileshipper) findet ihr mehr Informationen dazu.

Icinga Director-API