Seite wählen

NETWAYS Blog

RT Extensions Made Easy

Heute geht es darum, wie man auf einfache Art und Weise Erweiterungen für Request Tracker vorbereitet. Einen kleinen Ausblick darauf wie man sie dann auch schreibt, gibt es am Ende auch, aber mehr würde den Rahmen dieses Posts sprengen.
Bevor wir beginnen, müssen jedoch erst einige Vorbereitungen erledigt werden:
# cpanm Module::Install::RTx Dist::Zilla::MintingProfile::RTx
Dies installiert einige Werkzeuge die wir für die folgenden Beispiele benötigen.
Außerdem bietet es sich an, ein paar grundlegende Informationen über die eigene Person zu konfigurieren. Das erspart uns später einige Angaben:
$ dzil setup

Wer sich fragt was man alles mögliche an Lizenzen angeben kann, darf hier einen Blick riskieren.
Schon kann es losgehen. Zuerst erstellen wir mit dem sogenannten „profile provider“ RTx ein blankes Skelett.
Dies erstellt dort wo wir uns gerade befinden ein neues Verzeichnis mit dem Namen „RT-Extension-Netways“:
$ dzil new -P RTx RT-Extension-Netways

Die darin enthaltene Datei „Netways.pm“ nun öffnen und entsprechend anpassen bzw. erweitern. Darunter fallen i.d.R. der Name, die Beschreibung, die RT Versions-Voraussetzungen und die Autoren Angabe. Der Rest sollte bereits größtenteils vorausgefüllt sein, wie schon zuvor erwähnt.
Außerdem ist es ratsam sich die Datei „Makefile.PL“ einmal genauer anzusehen. Denn dort sind ebenfalls einige wichtige Angaben zu finden über deren Korrektheit man sich vergewissern sollte.
Hat man dies getan, kann man bereits mit der eigentlichen Entwicklung der Erweiterung beginnen.
Hierzu sei jedem die offizielle Dokumentation von Best Practical und HTML::Mason ans Herz gelegt.
Ist man letztendlich fertig mit der Entwicklung oder möchte schon einmal testen was man da tolles fabriziert hat, ist es an der Zeit die Verteilung seiner neuen Erweiterung vorzubereiten:
$ perl Makefile.PL

Da dies die erste Ausführung von „Makefile.PL“ war, wurden einige für die Installation notwendige Bibliotheken in die Struktur integriert. Diese sind notwendig, damit Nutzer die Erweiterung zumindest installieren können, ohne zusätzlich notwendige Abhängigkeiten.
Außerdem wurden einige zusätzliche Dateien und Verzeichnisse angelegt. Diese jedoch sind nur ein Nebenprodukt und nicht notwendig für die Verteilung. (Darunter das „Makefile“ und die „MYMETA.*“ Dateien.) Was alles genau nicht notwendig ist, kann in der Datei „gitignore“ nachgelesen werden. (Wer sowieso mit Git arbeitet, kann diese Datei auch zu „.gitignore“ umbenennen.)
Nun folgt man nur noch den üblichen Schritten und schon kann die Erweiterung konfiguriert und genutzt/getestet werden:
$ make
# make install

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.

Trainings mit Style

Wer bereits die Gelegenheit hatte an einem unserer Trainings teilzunehmen, dürfte bereits festgestellt haben, dass unsere Trainer keine schnöden Powerpoint Präsentationen verwenden.
Nein, sie verwenden showoff!
Markus hat bereits schon einmal darüber berichtet und showoff kurz vorgestellt, daher hier nur noch einmal eine kleine Zusammenfassung: showoff erlaubt es den Teilnehmern die Präsentation direkt in deren Browser nachzuverfolgen und auf weitere Inhalte zuzugreifen.
Bisher waren unsere Trainingsunterlagen jedoch von der showoff Version 0.9.11.1 abhängig. Diese ist mittlerweile mehr als zwei Jahre alt und die neuere Version bietet einiges an neuen Funktionen und natürlich Lösungen für bisherige Probleme. Nachdem ich mich letzte Woche damit beschäftigte diese Versionsabhängigkeit zu neutralisieren bzw. zumindest auf die aktuelle (v0.19.3) anzuheben, sind mir ein paar neue Features aufgefallen von denen unsere Trainer und natürlich Teilnehmer profitieren.

Live Code Execution
Live Slide Anmerkungen

Wer von was profitiert, lass ich mal offen. 😀

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.

Github Topics – Ein kleiner Blick in die Zukunft

Wir sind seit einiger Zeit damit beschäftigt, Icinga Exchange in neuem Licht erstrahlen zu lassen. Neben einem neuen Design wird es allerdings auch einige andere Änderungen und Neuerungen geben.
Eine dieser Neuerungen wird eine stärkere Github Integration sein. Wir werden unter anderem die bisher notwendige yaml Datei ablösen, indem wir Details über von Github synchronisierte Projekte über die API von Github abrufen. Dabei ist uns eine neue Funktion von Github ins Auge gefallen: Topics.
Diese ist seit Anfang des Jahres verfügbar und erlaubt es einem Repository bestimmte Begriffe zuzuordnen, ganz ähnlich den auf Icinga Exchange bekannten Tags. Mit der üblichen URL um Details zu einem Repository abzurufen wird man jedoch nicht fündig:

;


Aktuell fand diese Funktion noch keinen Einzug in die offizielle API. Stattdessen muss man explizit angeben, eine bestimmte preview Version der API benutzen zu wollen, damit zugeordnete Topics in den Details ebenfalls erscheinen.
Dies erreicht man bei Github mit bestimmten media types im Accept header:

Accept:application/vnd.github[.version].param[+json]

(Alle preview Versionen gibt es hier)
Um also über die API auf die Topics zuzugreifen, wird folgender media type benötigt:

Accept:application/vnd.github.mercy-preview


In der Antwort der API erscheint daraufhin ein neuer Eintrag:

{
  ...
  "topics": [
    "exchange",
    "icinga",
    "snmp"
  ],
  ...
}


Weil diese Funktion aber noch nicht Bestandteil der offiziellen API ist, werden wir vermutlich vorerst davon absehen diese in Icinga Exchange zu benutzen. Wir hoffen jedoch, dass sie bald Bestandteil der offiziellen wird oder absehbar ist, dass sie es sicher werden wird.
Ach, bevor Ihr fragt: Nein, wir haben noch keinen Release Zeitpunkt für die neue Version von Icinga Exchange. Aber vermutlich noch dieses Jahr. 😛

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.

Icinga Web 2 – Das gefällt jedem

Und wenn doch nicht, kann man es ganz einfach seinen eigenen Design-Vorstellungen entsprechend anpassen, denn darum geht es heute:

Theming

Wer es bis jetzt noch nicht entdeckt hat, darf nun erst einmal im Menü auf seinen Benutzernamen klicken und die Konto-Einstellungen öffnen. Dort versteckt sich seit v2.1.1 eine Möglichkeit das Theme, in dem Icinga Web 2 erstrahlt, zu ändern. (Sofern diese Möglichkeit nicht vom Administrator deaktiviert wurde.) Diese Themes werden teilweise von Icinga Web 2, teilweise von Modulen bereit gestellt können aber auch vom System-Administrator eingepflegt werden.
Von Haus aus stellt aktuell nur Icinga Web 2 zwei Themes bereit: High-Contrast und Winter.
Das High-Contrast Theme, wie der Name schon suggeriert, erhöht den Kontrast der Farben in Icinga Web 2. Es ist dafür gedacht die Barrierefreiheit zu erhöhen. Das Winter Theme hingegen ist einfach nur eine Spielerei für die kalten Winter-Tage.
Würde es aber heute nur um die einfache Nutzung der Themes gehen, wären wir nun schon wieder am Ende angelangt. Aber das wäre ja langweilig und für viele sicher auch nichts neues, nicht? Deshalb:

Eigene Themes erstellen

Erst einmal ein paar Grundsätze:

  • Globale Themes liegen hier: icingaweb2-installation/public/css/themes/
  • Modul Themes liegen hier: modul-installation/public/css/themes/
  • Der Name eines Themes wird direkt vom Dateinamen abgeleitet
  • Themes müssen die Datei-Endung „less“ haben
  • Geschrieben wird ein Theme mit CSS oder LESS

Auf CSS oder LESS gehen wir nun nicht näher ein, das wäre zu viel des Guten. Interessant allerdings dürfte sein, wie Stylesheets in Icinga Web 2 behandelt werden und was in den einzelnen bereits mitgelieferten Dateien enthalten ist.

Stylesheets in Icinga Web 2

Alle Stylesheets in den oben erwähnten Verzeichnissen, werden automatisch von Icinga Web 2 in eine einzelne Datei zusammengefasst und an den Browser ausgeliefert:

  • Mit Originaler (lesbarer) Formatierung: /icingaweb2/css/icinga.css
  • Keine Formatierung (Komprimiert): /icingaweb2/css/icinga.min.css

Da die Stylesheets von Icinga Web 2 immer zuerst kommen und danach die aller Module, können Themes (da sie als letztes kommen) alles andere beeinflussen.
Prinzipiell solltet ihr euch einfach einmal alle mitgelieferten Stylesheets ansehen: icingaweb2-installation/public/css/icinga/
Aber um den Überblick und die Orientierung etwas zu vereinfachen, ist hier eine grobe Zusammenfassung zu den wichtigsten Dateien:

  • base.less
    Allgemeine Regeln und alle globalen Farb-Variablen
  • login.less
    Regeln für den Login, inklusive welches Logo verwendet wird
  • layout.less
    Regeln für das globale Layout (Header, Container, Footer)
  • menu.less
    Regeln für das Haupt-Menü
  • forms.less
    Regeln für alle Formulare
  • tabs.less
    Regeln für die Tabs
  • controls.less
    Speziellere Regeln für Form-Elemente bzw. „Widgets“

Jetzt könnt ihr bereits loslegen. Einfach eine neue Datei in einem der oben genannten Pfade anlegen (je nachdem ob es sich um ein Modul handelt, oder nicht) und fröhlich drauf los stylen. Treten Probleme auf oder ihr möchtet mit den Developer-Tools des Browsers die Regeln inspizieren, kann es hilfreich sein den „_dev“ URL-Parameter an die URL anzuhängen. (z.B. /icingaweb2/dashboard?_dev) Dies führt dazu, dass das Stylesheet nicht komprimiert ausgeliefert wird.
Das wars dann nun aber. Falls noch Fragen aufkommen, verweise ich auf das Forum. Frohes schaffen!

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.

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:
[sourcecode language=“python“ wraplines=“false“ collapse=“false“]
t = threading.Thread(target=func)
t.daemon = True # Avoids that python hangs during shutdown
t.start()
[/sourcecode]
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:
[sourcecode language=“python“ wraplines=“false“ collapse=“false“]
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
[/sourcecode]
Kann ganz einfach, mit durchschlagendem Erfolg, so umgebaut werden:
[sourcecode language=“python“ wraplines=“false“ collapse=“false“]
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
[/sourcecode]
Der „CleanupThread“ sollte selbstverständlich keine Arbeit verrichten die unkontrolliert blockt. Denn dann sieht das ganze am Ende wieder so aus:
[sourcecode language=“python“ wraplines=“false“ collapse=“false“]
def _sigterm(self, signum, frame):
t = threading.Thread(target=self._cleanup, name=’CleanupThread‘)
t.daemon = True
t.start()
[/sourcecode]
Und der ganze Spaß geht von vorne los..

Johannes Meyer
Johannes Meyer
Lead Developer

Johannes ist seit 2011 bei uns und inzwischen, seit er 2014 die Ausbildung abgeschlossen hat, als Lead Developer für Icinga Web 2, Icinga DB Web sowie alle möglichen anderen Module und Bibliotheken im Web Bereich zuständig. Arbeitet er gerade mal nicht, macht er es sich bei schlechtem Wetter am liebsten zum zocken oder Filme/Serien schauen auf dem Sofa gemütlich. Passt das Wetter, geht's auch mal auf eines seiner Zweiräder. Motorisiert oder nicht.