JSON in bequem

Wenn man mit diversen Tools arbeitet, die wir in diesem Blog immer wieder bearbeitet, stößt man unweigerlich irgendwann auf JSON formatierte Texte. Während es sicher für den einen oder anderen IT-God kein Problem ist, das JSON im Hirn zu parsen und JSON-formatierter Text höchstens eine angenehme Erleichterung zu Bytecode ist, möchte ich hier kurz zweieinhalb Wege für Sterbliche vorstellen, um im
Wirrwarr von Klammern nicht die Übersicht zu verlieren.
Dabei bringen manche Dienste bereits eine Möglichkeit mit, den Output einfacher lesbar zu machen, andere verlassen sich dabei ganz auf externe Tools. Warum er nicht immer “einfach” gehalten wird, lässt sich ganz einfach damit erklären, das JSON eingentlich eh nur entworfen wurde, um von Maschinen verarbeitet zu werden und Menschen in den Wahnsinn zu treiben. Da sorgen Zeilenumbrüche und Einrückungen nur dafür, dass unnötig viele Daten übertragen werden.
Die REST API von Elasticsearch bietet je nach Endpunkt mal schön formatiertes, mal unformatierters JSON an.

# curl localhost:9200
{
  "name" : "y2G7v2X",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "1KYRb5mUQ8CaTSJDM-6djQ",
  "version" : {
    "number" : "6.3.2",
    "build_flavor" : "default",
    "build_type" : "rpm",
    "build_hash" : "053779d",
    "build_date" : "2018-07-20T05:20:23.451332Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

Dagegen sieht jeder Unterpunkt entsprechend unübersichtlich aus.

# curl localhost:9200/_cluster/health
{"cluster_name":"elasticsearch","status":"green","timed_out":false,"number_of_nodes":2,"number_of_data_nodes":2,"active_primary_shards":341,"active_shards":682,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}

Für Elasticsearch (und mittlerweile auch für Icinga 2!) gibt’s hier aber Abhilfe durch den Schalter ?pretty.

# curl localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
  "number_of_data_nodes" : 2,
  "active_primary_shards" : 341,
  "active_shards" : 682,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Da man diverse JSON Monster aber auch mal als Datei vorliegen hat oder nicht alle Dienste solche Komfortfunktionen bieten, braucht man immer wieder externe Hilfe. Dafür gibt’s die Allroundlösung, die überall funktioniert, wo Python installiert ist, also quasi bei jedem ernst zu nehmenden Betriebssystem.

# curl -s localhost:9200/_cluster/health | python -m json.tool
{
    "active_primary_shards": 341,
    "active_shards": 682,
    "active_shards_percent_as_number": 100.0,
    "cluster_name": "elasticsearch",
    "delayed_unassigned_shards": 0,
    "initializing_shards": 0,
    "number_of_data_nodes": 2,
    "number_of_in_flight_fetch": 0,
    "number_of_nodes": 2,
    "number_of_pending_tasks": 0,
    "relocating_shards": 0,
    "status": "green",
    "task_max_waiting_in_queue_millis": 0,
    "timed_out": false,
    "unassigned_shards": 0
}

Das war’s dann aber auch schon mit dem, was man einfach auf diese Weise machen kann. Oft reicht das ja auch aus. Wer aber gern etwas mehr an Möglichkeiten haben will, sollte sich unbedingt mal jq installieren.

# curl -s localhost:9200/_cluster/health | jq
{
  "cluster_name": "elasticsearch",
  "status": "green",
  "timed_out": false,
  "number_of_nodes": 2,
  "number_of_data_nodes": 2,
  "active_primary_shards": 341,
  "active_shards": 682,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 0,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 100
}

Soweit so fad. Ausser, dass ich auf meiner Shell noch schöne farbliche Hervorhebungen sehe und Ihr im Blog hier nicht. 😛
Interessant wird’s dann aber, wenn man mit jq anfängt, den Output auch gleich zu filtern. Dazu ein Auszug eines API Outputs von Icinga 2.

{
  "results": [
    {
      "attrs": {
        "__name": "canis",
        "acknowledgement": 0.0,
        "acknowledgement_expiry": 0.0,
[...]
    },
    "joins": {},
    "meta": {},
    "name": "canis",
    "type": "Host"
  },
[...]

Wenn man den Output dann durch einen jq Aufruf inkl. Filtern schickt, kann man auf die eigentlich interessanten Daten filtern. Ich hab’ mir freundlicherweise genehmigt, die folgenden Beispiele aus dem Icinga 2 Buch zu entnehmen.

$ curl ... |jq '{name: .results[].name}'
{
  "name": "canis"
}
{
  "name": "fornax"
}
{
  "name": "virgo"
}
[...]

Diese Filter kann man dann beliebig erweitern.

$ curl ... |jq '{name: .results[].name, address: .results[].attrs.address}'
{
  "name": "sculptor"
  "address": "172.16.2.11"
}
{
  "name": "fornax"
  "address": "172.16.1.11"
}
...

Eine umfassende Anleitung für jq gibt’s auch. Je nach Bedarf kann es sich aber auszahlen, sich erstmal diverse Tutorials anzusehen, da hier oft der Zugang etwas leichter gemacht wird.

Thomas Widhalm
Thomas Widhalm
Lead Support Engineer

Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 möchte er aber lieber die große weite Welt sehen und hat sich deshalb dem Netways Consulting Team angeschlossen. Er möchte ausserdem möglichst weit verbreiten, wie und wie einfach man persönliche Kommunikation sicher verschlüsseln kann, damit nicht dauernd über fehlenden Datenschutz gejammert, sondern endlich was dagegen unternommen wird. Mittlerweile wird er zum logstash - Guy bei Netways und hält...

atexit, oder wie man Python-Dienste nicht beenden sollte

Wer schon einmal einen Dienst mit Python realisiert hat, wird bereits vor der Aufgabe gestanden haben die Aufgaben die er verrichtet sauber und geordnet zu beenden. Python bietet einem hier vielerlei Lösungen an, darunter auch das atexit Modul. Für schnelle und simple Aufräum-Arbeiten ist dieses Modul wunderbar geeignet, nicht jedoch wenn Thread-Synchronisation und externe Kommunikation im Spiel ist. Warum das so ist und was die saubere Alternative ist, darum geht es heute in diesem Blogpost.
Wie der Name des Moduls und dessen Dokumentation bereits sagt, werden registrierte Routinen ausgeführt kurz bevor der Interpreter angehalten wird. Klingt erst einmal nicht besonders problematisch, ist es doch gerade was man haben möchte. Und es funktioniert sogar, solange keine Threads laufen. Dann werden die registrierten Routinen nicht aufgerufen. Das liegt daran, dass “normale” Threads explizit beendet werden müssen, sonst verhindern diese den Stopp des Interpreters. Damit das nicht passiert, kommt man möglicherweise auf folgende Idee:

t = threading.Thread(target=func)
t.daemon = True  # Avoids that python hangs during shutdown
t.start()

Ganz schlecht. Das mag möglicherweise den gewünschten Effekt haben und die Aufräum-Routinen können ihre Arbeit verrichten. Aber tatsächlich ist dies nur eine Lösung für ein Symptom, das eigentliche Problem ist noch immer nicht gelöst. Das wird einem spätestens klar, wenn es nicht mehr nur darum geht Threads zu beenden, sondern auch noch mit anderen über das Netzwerk verbundenen Diensten/Klienten eine saubere Unterbrechung der Kommunikation einzuleiten.
Denn was genau passiert wenn der Interpreter angehalten wird?

  1. Der MainThread wird sofort angehalten
  2. Offene File-Handles werden geschlossen
  3. Es wird gewartet dass alle nicht-Daemon Threads beendet wurden
  4. Die atexit-Routinen werden ausgeführt
  5. Garbage Collection
  6. Der Prozess wird beendet

Der zweite Punkt lässt einige vielleicht bereits aufhorchen, denn sockets sind nichts anderes als File-Handles. Wer nun immer noch denkt er könne in einem daemon-Thread die Netzwerk-Kommunikation mit seinem Gegenüber sauber beenden, ist auf dem Holzweg. Zum Zeitpunkt zu dem die atexit-Routinen laufen, sind bereits alle sockets unwiderruflich geschlossen.
Angenommen der Stopp des Dienstes wird ganz normal über SIGTERM initiiert, so könnte man nun alle atexit-Routinen in einen Signal-Handler verlagern. Dadurch würden sie laufen noch bevor der Interpreter angehalten wird, allerdings kommt man so sehr schnell wieder in die Bredouille, je nachdem welche Art von Arbeit der MainThread verrichtet. Denn da Signal-Handler asynchron im MainThread ausgeführt werden, ist das Risiko für ein Deadlock sehr groß. Kommen wir also zur erwähnten, sauberen Alternative: Feuer mit Feuer bekämpfen.
Was vormals in etwa so aussah:

class SomeDaemon:
    def start():
        # ...
        signal.signal(signal.SIGTERM, self._sigterm)
        atexit.register(self._atexit)
        # ...
    def _sigterm(self, signum, frame):
        sys.exit(0)
    def _atexit(self):
        # Alle Aufräum-Arbeiten

Kann ganz einfach, mit durchschlagendem Erfolg, so umgebaut werden:

class SomeDaemon:
    def start():
        # ...
        signal.signal(signal.SIGTERM, self._sigterm)
        atexit.register(self._atexit)
        # ...
    def _sigterm(self, signum, frame):
        threading.Thread(target=self._cleanup, name='CleanupThread').start()
    def _cleanup(self):
        # Komplexe Aufräum-Arbeiten
    def _atexit(self):
        # Einfache Aufräum-Arbeiten

Der “CleanupThread” sollte selbstverständlich keine Arbeit verrichten die unkontrolliert blockt. Denn dann sieht das ganze am Ende wieder so aus:

    def _sigterm(self, signum, frame):
        t = threading.Thread(target=self._cleanup, name='CleanupThread')
        t.daemon = True
        t.start()

Und der ganze Spaß geht von vorne los..

Johannes Meyer
Johannes Meyer
Developer

Johannes ist seit 2011 bei uns und hilft bei der Entwicklung zukünftiger Knüller (Icinga2, Icinga Web 2, ...) aus dem Hause NETWAYS.

Graphite installation unter Debian 8.4

Ich hab mich mit der Aktualiserung unserer Schulungsunterlagen diese Woche beschäftigt, dies erfolgt bei uns auf kontinuierlicher Basis.
Was mir dabei auffiel bei unseren Graphing Unterlagen war dass das Webfrontend von Graphite wenn man es unter einer aktuellen Version von Debian oder Ubuntu installiert den Zugriff auf die Weboberfläche verweigert. Ich warf ich einen genaueren Blick auf das Setup um zu sehen wo der Fehler lag.
Die Ursache war relativ schnell ausgemacht.
Unter Debian Jessie 8.4 und Ubuntu 15.10 in den aktuellen Versionen gibt es kleine Veränderungen in der Apache2-Konfiguration unter Apache 2.4.10 und der Installation von Graphite.
Der bisherige Installationsweg für eine Graphite Installation mit Webfrontend war bisher wie folgt:

# apt-get install apache2 libapache2-mod-wsgi python-django python-django-tagging fontconfig python-tz python-pip python-dev python-twisted python-cairo
# pip install carbon whisper graphite-web
# pip install --upgrade twisted
# cp /opt/graphite/conf/carbon.conf.example /opt/graphite/conf/carbon.conf
# cp /opt/graphite/conf/storage-schemas.conf.example /opt/graphite/conf/storage-schemas.conf
# /opt/graphite/bin/carbon-cache.py start
# /opt/graphite/bin/carbon-cache.py status
# cp /opt/graphite/examples/example-graphite-vhost.conf /etc/apache2/sites-available/graphite.conf
# cp /opt/graphite/conf/graphite.wsgi.example /opt/graphite/conf/graphite.wsgi
# cp /opt/graphite/webapp/graphite/local_settings.py.example \ /opt/graphite/webapp/graphite/local_settings.py
# python /opt/graphite/webapp/graphite/manage.py syncdb
# chown www-data. -R /opt/graphite/storage
# a2dissite 000-default
# a2ensite graphite
# a2enmod wsgi
# service apache2 restart
# chmod +x /etc/init.d/carbon-cache // Damit 'carbon-cache.py start' mit Systemstart erfolgt

Die *kleine* aber wichtige Änderung ist folgende pip install graphite-web fügt nicht folgendes hinzu:
Anscheinend vergisst die pip graphite-web den Eintrag in der graphite-vhost.conf welcher auf das webapp Verzeichnis den Zugriff erlaubt.

// < Directory "/opt/graphite/webapp/" >
// Options +ExecCGI
// Order deny,allow
// Allow from all
// Require all granted
// < /Directory >

Wenn man dies wie manuell ergänzt dann erhält am auch Zugriff auf die Weboberfläche.
Hier ein Kurzdurchlauf bei der Graphite installation auf einem frischen Debian Jessie 8.4.

# apt-get install python python-pip python-dev python-cairo python-django \
python-django-tagging apache2 libapache2-mod-wsgi python-twisted \
python-memcache python-pysqlite2 python-simplejson
# pip install whisper
# pip install carbon
# pip install graphite-web
# cp /opt/graphite/webapp/graphite/local_settings.py.example \ /opt/graphite/webapp/graphite/local_settings.py
# cp /opt/graphite/conf/graphite.wsgi.example /opt/graphite/conf/graphite.wsgi
# cp /opt/graphite/conf/carbon.conf.example /opt/graphite/conf/carbon.conf
# cp /opt/graphite/conf/storage-schemas.conf.example /opt/graphite/conf/storage-schemas.conf
# cp /opt/graphite/conf/storage-aggregation.conf.example \
/opt/graphite/conf/storage-aggregation.conf
# cd /opt/graphite/webapp/graphite && python manage.py syncdb
# chown -R www-data:www-data /opt/graphite/storage/
# vim /etc/apache2/sites-available/graphite.conf // hier erfolgt das manuelle Hinzufuegen von des Ordners in der Config
# a2dissite 000-default.conf
# a2ensite graphite.conf
# service apache2 restart
# vim /etc/systemd/system/carbon-cache.service // Erstellen eines Service-Files fuer den Start von carbon-cache.py start
# systemctl enable carbon-cache.service
# systemctl start carbon-cache.service
# systemctl reload apache2.service

Somit ist das Problem gelöst und die Graphite-Web Oberfläche aufrufbar.
Ich werde demnächst noch ein Video fuer Ubuntu 15.10 nachreichen da hier die Pfade abweichen von Debian und dies nicht 1:1 übertragbar ist.
Bis Demnächst!
Und übrigens: die nächste Graphing Schulung findet schon ganz bald statt, also noch fix anmelden!

David Okon
David Okon
Senior Consultant

Weltenbummler David hat aus Berlin fast den direkten Weg zu uns nach Nürnberg genommen. Bevor er hier anheuerte, gab es einen kleinen Schlenker nach Irland, England, Frankreich und in die Niederlande. Alles nur, damit er sein Know How als IHK Geprüfter DOSenöffner so sehr vertiefen konnte, dass er vom Apple Consultant den Sprung in unser Professional Services-Team wagen konnte. Er ist stolzer Papa eines Sohnemanns und bei uns mit der Mission unterwegs, unsere Kunden zu...

Bottle – Klein aber fein

Vor einiger Zeit war es notwendig für ein Kunden-Projekt dessen Umgebung bei uns nachzubilden. Es war von vornherein klar, dass es keine 1:1 Nachbildung sein muss (kann) und somit war es nur notwendig einige wenige Bestandteile einer REST Api zu simulieren. Da REST rein HTTP basiert ist und die zu simulierenden Aktionen nur GET waren, kam uns erst gar nicht in den Sinn ein großartiges PHP-Projekt ins Leben zu rufen und entschieden uns daher für eine schnelle Lösung mittels eines Standalone Python Skriptes.
Mir war Bottle schon seit einigen Jahren bekannt und deshalb noch als Werkzeug für schnelle und einfache Lösungen in Erinnerung. Tatsächlich eingesetzt hatte ich es allerdings noch nie, weshalb dies die Chance war es endlich einmal zu tun. Gesagt getan, runtergeladen und ausgeführt. Da Bottle allein auf der Standard-Library von Python basiert und mit Python 2.6+ wie auch Python 3.2+ kompatibel ist, funktionierte das sofort ohne Probleme. Und es hält was es verspricht!
Allein das erste Beispiel auf seiner Webseite zeigt bereits wie schnell ein Web-Server mit dynamischem Routing und Inhalten aufgesetzt wird:

from bottle import route, run, template
@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)
run(host='localhost', port=8080)

Ja sogar Basic Authentication beherrscht es:

from bottle import route, run, template, auth_basic
def authenticate(username, password):
    return username == 'max' and password == 'mustermann'
@route('/hello/<name>')
@auth_basic(authenticate)
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)
run(host='localhost', port=8080)

Ist das nicht toll? Ich nehme stark an, dass da noch mehr geht, aber für den Moment bin ich vollauf zufrieden. 🙂

Johannes Meyer
Johannes Meyer
Developer

Johannes ist seit 2011 bei uns und hilft bei der Entwicklung zukünftiger Knüller (Icinga2, Icinga Web 2, ...) aus dem Hause NETWAYS.

Monthly Snap November: OSMC, Icon Fonts & Jaspersoft Studio

November presented an exciting 10th OSMC, featured Jasper reporting and offered DevOps tips for sys admins, developer and tinkerer.weekly snap
Beginning with events, our 10th Open Source Monitoring Conference with many special guests took place and Daniela reported on day one and Dirk summarized the other days of the conference while Jean-Marcel gave us an overview of his two favorite presentations.
Eric then described how to create Icon-Fonts and Alexander explained how to upgrade python-driven servers.
Lastly, Christoph looked at Jaspersoft Studio and Kay shared a guide to control home automation components with Rasperry PI and a web interface.

Stephanie Kotilge
Stephanie Kotilge
Accountant

Steffi kümmert sich bei NETWAYS ums Office-Management und ist die gute Seele der Fabrik. Auch unsere Kunden kommen an ihr nicht vorbei und wollen dies natürlich auch nicht. Da sie ein überdurchschnittliches sprachliches Talent besitzt und neben Deutsch noch fliessend in Englisch und Französisch unterwegs ist, kümmert sie sich auch um einen Großteil der Übersetzungen. In diesem Sinn ... Merci

powerline-shell

powerline-shell
Hi folks,
ich habe letztens bei meinem Kollegen Mr. Frog ein neues Bling-Bling entdeckt The Mighty Powerline-Shell die mir besonders gut gefallen hat, ich war so angefixt das Ich mir einbildete das auch haben zu müssen, gesagt getan.
Die Powerline-Shell ist richtig Gut, da Sie als Segmentiertes ‘self contained’ Python-Script folgende Features abdeckt.

  • Ihr seht egal ob Git, SVN, Mercurial o. Fossil immer was gerade Sache ist
  • wenn eure Pfade auf einer Zeile zu lang werden, dann werden Sie gekürzt ohne relevante Information zu verlieren
  • viel Buntes für den Kreativen Ops/Dev ( Colored Themes )
  • auch werden euch Rückgabe werte des letzten Befehls aus eurer Shell ungleich 0 zurückgegeben
  • wenn Ihr euch per SSH auf entfernte Systeme verbindet, dann wird auch dies durch ein Schloss Symbol kenntlich gemacht
  • wenn das aktuelle Verzeichnis nicht mit euren Rechten beschrieben werden darf, wird dies auch durch ein Schloss Symbol wiedergegeben
  • Sie kann auch den Title eures TerminalEmulator mit user@host:/path Einträgen umschreiben
  • zeigt auch Änderungen an der Umgebung an ( Virtual Environment )
  • die aktuelle Zeit ist nun auch mit am Start ( Segment wurde von mir eingeführt, da ich dieses Feature für mein Daily benötige )
  • zeigt auch laufende Hintergrund Jobs an ( Shell forks )
  • die Features sind leicht zu erweitern ( Segmente werden in Python geschrieben )

Die Powerline-Shell ist auch sehr benutzerfreundlich in etwa 3 Schritten eingerichtet.

cd ~ && mkdir git && cd git
git clone https://github.com/powerline/fonts.git powerline-shell-fonts
git clone https://github.com/lenrico/powerline-shell.git
./powerline-shell-fonts/install.sh
./powerline-shell/install.py
vi ~/.bashrc

Noch fix den Prompt auf die Powerline-Shell umbiegen. 😉

function _update_ps1() {
	export PS1="$(~/powerline-shell.py $? 2>> /dev/null)"
}
export PROMPT_COMMAND="_update_ps1; $PROMPT_COMMAND"

Und einen der neu installierten Fonts mit dem ‘Powerline’ Suffix wählen, fertig.
Hier noch Bilder “weil Bilder mehr als tausend Worte …” naja ihr wisst schon.
github-lenrico-powerline-shell
Links:
https://github.com/lenrico/powerline-shell
https://github.com/Lokaltog/powerline-fonts
 

From Perl to Python and beyond

I’ve seen and learned plenty of programming languages either during my studies or in work or spare time related projects – be it C/C++/C#, VHDL, Java or PHP/Perl/Python/Ruby even (I’ve removed some in my XING profile to reduce recruiter spam level ;)). Choosing the “right” language is always hard but most of the time the requirements of existing software or newly designed projects allow you to skip that part and already have one in mind.
python-logo-master-v3-TMWhen joining NETWAYS in late 2012, I took over the LConf backend project being entirely written in Perl similar to other plugins and scripts floating around as open source software. Icinga 1.x Core is partially using Perl as well (although embedded Perl in C is a horrible mess). Most recently our development team decided to go for Python as the primary programming language.
So, my Python foo wasn’t that good after some years not really using it. Learning from my colleagues and looking into additional ressources helped a lot. I also found “Head first Python” from O’Reilly in my bookshelf as a gift from the Nagios/Icinga cookbook review which also provides an extensive introduction into Python – when you already know a scripting language like Perl.
We’ve been working on a customer’s project developing a plugin framework for executing checks via a defined transport (e.g. ssh) and parsing cli output. Find some collected hints and tricks I’ve learned below.

Remove colors from shell output

The EMC “isi” cli command puts colors into the shell output (e.g. for a critical state turning the background red). While one could disable color support on the shell, this was not possible in that environment. The fix by Gunnar was easy: Parsing text output into a list called “lines” and clean the line string from shell color codes:

lines = text.split('\n')
lines = [re.sub('\x1b\[[0-9;]*m', '', line).strip() for line in lines]

Convert datetime to unix timestamp

Consider having a datetime string whose output format is not fixed, which means some strftime print magic does not work for proper parsing. Luckily there’s a Python module called dateutil providing the required functionality.

import time
import datetime
import dateutil.parser
def datetime2unixts(time_str):
    dt = dateutil.parser.parse(time_str)
    return time.mktime(dt.timetuple())
$ python
Python 2.7.8 (default, Apr 15 2015, 09:26:43)
[GCC 4.9.2 20150212 (Red Hat 4.9.2-6)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import time, datetime, dateutil.parser
>>> def datetime2unixts(time_str):
...     dt = dateutil.parser.parse(time_str)
...     return time.mktime(dt.timetuple())
...
>>> time_str = "Wed May 13 16:15:34 CEST 2015"
>>> datetime2unixts(time_str)
1431526534.0

Initialize nested dictionary

Similar to what I am used to do with Perl and hashes I was stumbling over this with Python. In Perl one does not care about initializers, but just keeps populating all items like so:

my $hash = {}
$hash{'key1'}{'key2'} = "val1";

In Python this does not work out-of-the-box. There are some modules available such as “collectors”, but I prefer a somewhat native implementation making it work on older distributions. I came across this solution which I consider pure awesome 🙂

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value
hash = AutoVivification()
hash["key1"]["key2"] = "val1"

Note: It certainly does create non-existing keys if you are checking for their existance. My implementation does not care about key checks, but requires known-to-exist values from a flat dictionary with “id_label” as key stashed into a nested dictionary where id and label are separated/nested for further operations.

        jobs = AutoVivification()  # use a pre-initialized nested dict
        for k, v in sorted(perfdata.iteritems()):
            (job_id, label) = k.split("_", 1)
            if label == "ended_ts":
                jobs[job_id][label] = float(v)
            if label == "started_ts":
                jobs[job_id][label] = float(v)

Fix the indent

If your editor doesn’t do that automatically (e.g. converting tabs into 4 spaces) it might still work, but could also cause weird behaviour in Python depending on the indent only (no brackets as I am used to with Perl). Fixing this is fairly easy by using autopep8 – I’m using Fedora 22:

dnf install python-autopep8
autopep8 check_plugin -i

Note: ‘-i’ replaces the given file with all the changes. Make sure to commit your other changes to git before.

Conclusion

I never thought that I would dig deep in Python again that easy. I used to hack that in the past with Win2k usb driver test frameworks, and also checkmk plugins, but gaining back experience worked out pretty well.
Icinga 2’s code, value types and configuration look a lot like Python as well – take dictionaries for custom attributes using apply for loops, eh? (Hint: Testdrive this config snippet inside the Icinga 2 Docker container).

object Host "dns" {
  import "generic-host"
  address = "127.0.0.1"
  address6 = "::1"
  vars.dns_checks["dns icinga.org"] = {
    dns_lookup = "icinga.org"
    dns_server = "ns1.netways.de"
    dns_expected_answers = "185.11.254.83"
  }
  vars.dns_checks["dns netways.org"] = {
    dns_lookup = "netways.org"
    dns_server = "ns1.netways.de"
    dns_expected_answers = "185.11.252.37"
  }
}
apply Service for (dns_check => config in host.vars.dns_checks) {
  check_interval = 1m
  retry_interval = 30s
  check_command = "dns"
  vars += config
}

Icinga 2, plugins, your project – here I come 🙂
PS: See you at the OSMC hackathon.

Michael Friedrich
Michael Friedrich
Senior Developer

Michael ist seit vielen Jahren Icinga-Entwickler und hat sich Ende 2012 in das Abenteuer NETWAYS gewagt. Ein Umzug von Wien nach Nürnberg mit der Vorliebe, österreichische Köstlichkeiten zu importieren - so mancher Kollege verzweifelt an den süchtig machenden Dragee-Keksi und der Linzer Torte. Oder schlicht am österreichischen Dialekt der gerne mit Thomas im Büro intensiviert wird ("Jo eh."). Wenn sich Michael mal nicht in der Community helfend meldet, arbeitet er am nächsten LEGO-Projekt oder geniesst...

OSMC 2014: Der Countdown läuft – nur noch 120 Tage

Mit Georg Kostner geht es im heutigen Video-Countdown um Al’exa.

OSMC? Was soll das denn sein und wer sind die netten Menschen in diesen Videos? Die Open Source Monitoring Conference (kurz: OSMC) ist die internationale Plattform für alle an Open Source Monitoring Lösungen Interessierten, speziell Nagios und Icinga. Jedes Jahr gibt es hier die Möglichkeit sein Wissen über freie Monitoringsysteme zu erweitern und sich mit anderen Anwendern auszutauschen. Die Konferenz richtet sich besonders an IT-Verantwortliche aus den Bereichen System- und Netzwerkadministration, Entwicklung und IT-Management. Und die netten Menschen, die Ihr in unseren Videos zur OSMC seht, gehören dazu. 2014 wird die OSMC zum 9. Mal in Nürnberg stattfinden.

Weekly Snap: OSDC 2014, Puppet Camp & GnuPG/GPG Best Practices

weekly snap7 – 11 April was OSDC 2014 and PuppetCamp week, with a few GPG best practices and Python musings slipped in.
Eva ended her countdown to the OSDC with Daniel Kirstenpfad’s talk on ‘Why Virtual Development and Testing isn’t Rocket Science’ and thanked all attendees and sponsors in advance. Dirk and Michael then followed with their highlights from the first and second days at the OSDC 2014.
Continuing with events, Christian announced the next round of webinars featuring Request Tracker, Puppet, Bareos and inGraph and Dirk returned with his review of Puppet Camp 2014.
Thomas then gave a comprehensive rundown of GnuPG / GPG best practices as Alexander shared his thoughts on Python 2.4 and RHEL.
Finally, Georg ended the week by introducing the newest addition to our hardware store: HW Group Poseidon2 3266.

Weekly Snap: PuppetConf Photos, Shell File Transfers & SQL Triggers

weekly snap19 – 23 August shared Shell and SQL tips, and impressions from the OSMC past, PuppetConf of the present and OpenNebula Conf to come.
Eva began by counting 67 days to the OSMC 2013 with Team Icinga’s presentation from last year.
She went on to remind interested participants to book their tickets for the OpenNebula Conf on 24 – 26 September and snap up the remaining gold packages for accommodation at the conference hotel.
Marius G then discovered how to transfer files via Shell and Python while Matthias demonstrated the power of SQL triggers.
To close the week, Tobias played his third round of IT buzzword bingo as Bernd reported live from PuppetConf 2013 in San Francisco with his impressions of the first day.