Seite wählen

NETWAYS Blog

Arbeiten mit dem vorangegangen Bash-Kommando

Neben nützlichen Tastenkürzeln wie Ctrl+K, um alles von der Position des Cursors bis zum Ende der Zeile zu löschen oder Ctrl+W um das Wort vor dem Cursor zu löschen, bietet die Bash viele andere Funktionen, die die Arbeit komfortabler gestalten.
Nochmal bitte
Wer kennt das nicht? Das gerade eben ausgeführte Kommando benötigt Root-Rechte, man hat aber sudo vergessen. Glücklicherweise kann es aber ohne große Umwege erneut aufgerufen werden – diesmal mit sudo:

$ sudo !!

Vertippt?
Um Tippfehler zu korrigieren oder Argumente zu manipulieren – das vorhergehende Kommando kann per Suchen und Ersetzen, vor dem erneuten Ausführen verändert werden:

$ ehco vertippt
bash: ehco: command not found...
Similar command is: 'echo'
$ ^ehco^echo
echo vertippt
vertippt

Argumente erneut benutzten
Die Argumente des vorherigen Kommando können einfach wiederverwendet werden:

$ mkdir /tmp/neuer_ordner
$ cd !*

Wer nützliche Kommandos sucht, von denen er noch nichts weiß :-), dem empfehle ich zum Weiterlesen commandlinefu.

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.

Zend JSON-Ausgabe

Um JSON mit dem Zend auszuliefern, könnte man auf die harte Tour, Header selbst setzen, Daten enkodieren und layouts deaktvieren oder man nutzt Funktionalitäten, die das Framework schon bietet. Um zum Beispiel JSON direkt zu senden, bedient man sich Zend_Controller_Action_Helper_Json:

class TestController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $data = array(
            'test' => true
        );
        /**
         * Call Zend_Controller_Action_Helper_Json::direct
         */
        $this->_helper->json($data);
    }
}

Über die Url ../test erhalten wir die Ausgabe in JSON inklusive dem korrekten Content-type application/json.
Nachteil dieses Aufruf ist, dass die dispatch loop sofort unterbrochen wird und somit eventuelle Erweiterungen durch Plugins nicht mehr aufgerufen werden.
Besser ist es, den ContextSwitch Action-Helfer zu benutzen, welcher XML und JSON unterstützt, layouts deaktiviert und Header enstrechend setzt:

class TestController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper
             ->contextSwitch()
             ->addActionContext('index', 'json')
             ->initContext();
    }
    public function indexAction()
    {
        $this->view->test = true;
    }
}

Die JSON-Ausgabe ist nun über die Url ../test?format=json erreichbar.
Um sich den format-Parameter zu sparen, kann man für alle actions im Controller das json-Format erzwingen:

public function init()
{
    /**
     * Force json format
     */
    $this->_request->setParam('format', 'json');
    ...
}

Zum weiterlesen: Zend ContextSwitch

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.

Der Python Debugger

Das Python-Modul pdb stellt einen interaktiven Debugger für Python-Programme zur Verfügung. Es umfasst den Quelltext in Einzelschritten zu evaluieren, Variablen zu inspizieren, beliebigen Python-Code an jeder Stelle auszuführen und das sogenannte „post-mortem debugging“.
Debugger Starten
Frei nach dem Motto: „viele Wege führen nach Rom“ – gibt es verschiedene Wege den Debugger zu starten. Je nach Bedarf, wo und was zu debuggen ist, entscheidet der Benutzer, wann der Python-Interpreter den Debugger „betritt“.
Von der Kommandozeile
Die Eingabeaufforderung des Debuggers ist (Pdb).
Rufen wir den Debugger von der Kommandozeile aus auf, lädt dieser unser Skript pdb_test.py und stoppt bei der ersten Anweisung:

$ python -m pdb pdb_test.py
> .../pdb_test.py(3)()
-> def test():
(Pdb)

Im Interpreter
Experimentiert man im interaktiven Interpreter, ist es hilfreich den Debugger auch hier via pdb.run oder pdb.runeval starten zu können:

$ python
Python 2.7.2 (default, Oct 27 2011, 01:40:22)
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> import pdb_test
>>> pdb.run('pdb_test.test()')
> <string>(1)<module>()
(Pdb)

Im Programm
Die oben genannten Möglichkeiten sind in den meisten Fällen nur dann hilfreich, wenn das entwickelte Programm überschaubar ist oder Probleme schon am Anfang der Ausführung auftreten. Stößt man auf Fehler in größeren Programmen oder erst später in der Ausführung, ist es von Vorteil, den Debugger im Programm zu starten:

#!/usr/bin/env python
import pdb
def test():
    pdb.set_trace()
    print "Hallo!"
if __name__ == '__main__':
    test()

Der Funktionsaufruf pdb.set_trace() in Zeile 6 startet den Debugger, wenn die Instruktion erreicht wird:

$ python pdb_test.py
> .../pdb_test.py(7)test()
-> print "Hallo!"
(Pdb)

Nach einem Fehler
Programme nach einem Fehler zu debuggen, nennt sich „post-mortem debugging“. pdb unterstützt das durch die Funktionen pm() und post_mortem().

class Output(object):
    def __init__(self, output):
        self.the_output = output
    def echo(self):
        print self.output

In Zeile 7 versteckt sich ein AttributeError, da wir eigentlich self.the_output ausgeben müssten. Wird die besagte Zeile aufgerufen, stoppt die Ausführung mit dem entsprechenden Fehler. Danach können wir pdb.pm() aufrufen, um den Debugger an der Stelle zu starten, an der die Exception aufgetreten ist:

$ python
Python 2.7.2 (default, Oct 27 2011, 01:40:22)
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_test
>>> pdb_test.Output("Hallo!").echo()
Traceback (most recent call last):
  File "", line 1, in 
  File "pdb_test.py", line 11, in echo
    print self.output
AttributeError: 'Output' object has no attribute 'output'
>>> import pdb
>>> pdb.pm()
> .../pdb_test.py(11)echo()
-> print self.output
(Pdb)

Debugger benutzen
Der Debugger versteht einen definierten Satz an Befehlen – alle anderen Anweisungen werden als Python-Code behandelt und im Kontext des Programm ausgeführt. So können sogar Variablen und Funktionen während dem Debugging verändert werden. Die Eingabe einer leeren Zeile wiederholt den letzten eingegeben Befehl.
Navigation
w(here): Anzeigen der Position im call stack und nächste auszuführende Instruktion:

(Pdb) where
  /usr/lib64/python2.7/bdb.py(387)run()
-> exec cmd in globals, locals
  (1)()
> .../pdb_test.py(3)()
-> import pdb

l(ist) [start[, ende]]: Auruf ohne Argumente zeigt 5 Zeilen vor und nach der aktuellen Zeile. Mit einem Argument werden 5 Zeilen vor und nach der angegebenen Zeile angezeigt und mit 2 Argumenten der angegebene Bereich:

(Pdb) list
  1  	#!/usr/bin/env python
  2
  3  ->	import pdb
  4
  5  	class Output(object):
  6
  7  	    def __init__(self, output):
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
(Pdb) l 13
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
 12
 13  	if __name__ == '__main__':
 14  	    Output("Hallo!").echo()
[EOF]
(Pdb) l 5, 14
  5  	class Output(object):
  6
  7  	    def __init__(self, output):
  8  	        self.the_output = output
  9
 10  	    def echo(self):
 11  	        print self.output
 12
 13  	if __name__ == '__main__':
 14  	    Output("Hallo!").echo()

u(p), d(own): Aktuelle frame im call stack nach oben (up, älter) oder unten (down, neuer) verschieben:

(Pdb) up
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) d
> .../pdb_test.py(11)echo()
-> print self.output

Schrittweise Ausfhüren
s(step): Führt die aktuelle Zeile aus und stoppt bei der nächsten aufzurufenden Funktion oder Zeile in der aktuellen Sequenz:

(Pdb) ...
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) s
--Call--
> .../pdb_test.py(7)__init__()
-> def __init__(self, output):

n(ext): Verhält sich ähnlich wie step, stoppt aber nicht bei Funktionen die von der auszuführenden Anweisung aufgerufen werden:

(Pdb) ...
> .../pdb_test.py(14)()
-> Output("Hallo!").echo()
(Pdb) n
AttributeError: "'Output' object has no attribute 'output'"
> /tmp/pdb_test.py(14)()
-> Output("Hallo!").echo()

r(eturn): Stoppt bei der nächsten return-Anweisung:

#!/usr/bin/env/python
import random
def randint(a, b):
    random_int = random.randint(a, b)
    return random_int
if __name__ == '__main__':
    random.seed()
    print randint(0, 100)
(Pdb) ...
> .../pdb_return.py(11)()
-> print randint(0, 100)
(Pdb) s
--Call--
> .../pdb_return.py(5)randint()
-> def randint(a, b):
(Pdb) r
--Return--
> .../pdb_return.py(7)randint()->58
-> return random_int

Variablen überprüfen
a(rgs): Zeigt die Argumente der aktuellen Funktion und
p expression: Evaluiert expression und zeigt dessen Ausgabe, zum Beispiel den Wert einer Variable:

(Pdb) ...
> .../pdb_return.py(11)()
-> print randint(0, 100)
(Pdb) s
--Call--
> .../pdb_return.py(5)randint()
-> def randint(a, b):
(Pdb) r
--Return--
> .../pdb_return.py(7)randint()->24
-> return random_int
(Pdb) args
a = 0
b = 100
(Pdb) p random_int
24

Natürlich unterstützt der Debugger auch Breakpoints und jump – dazu schreibe ich vielleicht etwas in einem zweiten Teil.
Weiterführende Informationen sind in der Python Dokumentation und den absolut genialen Abhandlungen von Doug Hellmann, namens Python Module of the Week, zu finden, dessen Gliederung ich hier mal ganz frei übernommen habe.

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.

inGraph 1.0 released

inGraph - LogoNach vielen Monaten (eigentlich Jahren) steht seit heute die finale Version 1.0 unseres Graphing-Tools inGraph zum Download bereit. Seit dem Beta-Release konnten wir viel Erfahrung, sowohl durch Communityfeedback, als auch bei großen Kundeninstallation sammeln und diese in die Release-Version einfliessen lassen. Die Performance und Skalierbarkeit des Collectors konnten wir dadurch noch weiter verbessern, viele Bugs beseitigen und Templates für die wichtigsten Checks erstellen (die natürlich bei Bedarf einfach an die individuellen Bedürfnisse angepasst werden können).
Zeitgleich beenden wir auch die Arbeit an unseren bisherigen Graphing-Tools NagiosGrapher und NETWAYS Grapher V2, wobei letzterer auch unsere Erwartungen nie erfüllen konnte. Somit war die Entscheidung klar, nochmal neu zu starten und alle gewonnen Erkenntnisse in inGraph zu stecken.
Neben den bekannten Features der verfügbaren Graphing-Werkzeuge bietet inGraph die Möglichkeit eventbezogene Kommentare zu speichern, Templates im Frontend anzupassen und Linien ein- und auszublenden. Die Verwendung des Plugins check_ingraph (das sich im bin-Ordner befindet) bietet darüber hinaus die Möglichkeit verschiedene Zeiträume aus Sicht der Performance zu vergleichen. So kann Icinga oder Nagios alarmieren, wenn die Load zum gegebenen Zeitpunkt beispielsweise 15% höher ist als gewöhnlich.


Wir sind stolz auf das Release und sind überzeugt das sich unsere Arbeit sehen lassen kann: Mit inGraph betritt eine flexible und leicht skalierbare Graphing Lösung das Icinga/Nagios Umfeld. Durch die Kombination von flexiblen Frontend, performanten Backend und leichter Anpassbarkeit bietet es alles, was wir an bestehenden Graphinglösungen bisher vermisst haben.
Die Installationsroutine, sowie diverse Einstellungsmöglichkeiten und Template-Syntax sind in der ausführlichen Dokumentation zu finden. Die in der Beta-Release mitgelieferten Templates haben wir komplett überarbeitet und bieten so einen guten Einstieg um weitere Anpassungen zu machen. Wir freuen uns natürlich über Templates aus der Community und übernehmen diese gerne in den Standard.

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.

Doctrine 1.2 und Nested Sets

Über Doctrine schreibe ich ja gerne mal was, so auch heute. Da Doctrine 2.2 die letzte aktuelle Version ist, hoffe ich, dass es noch andere gibt, die ältere Versionen einsetzen und folgende Informationen vielleicht nützlich finden.
Kindknoten hinzufügen
Ein Nested Set zu speichern, bei dem die Sortierung der Knoten der Reihenfolge entspricht, wie sie hinzugefügt werden, sieht auf den ersten Blick ganz einfach aus:


$node->getNode()->insertAsLastChildOf($parent);

Aber: Doctrine ändert bei diesem Funktionsaufruf automatisch die Grenzen der Teilmenge (links und rechts), ausgehend von $parent, in der Datenbank. Die Grenzen vom Objekt $parent werden aber nicht aktualisiert. Das führt dazu, dass neue Knoten nicht als letztes, sondern als erstes Kind hinzugefügt werden. Ein simples


$parent->refresh();

nach jeder Änderung, löst das Problem.
Teilbäume
Zum Abschluss, stell ich noch Code zur Verfügung, der einen Teilbaum aus einem Nested Set, mit dem Namen Tree, selektiert:


$table = Doctrine_Core::getTable('Tree');
$tree = $table->getTree();
$query = $table->createQuery('t, Tree p')
               ->select('t.*')
               ->orderBy('t.lft ASC')
               ->where('p.id = ?', $id)
               ->andWhere('t.root_id = p.root_id')
               ->andWhere('t.level >= p.level')
               ->andWhere('t.lft BETWEEN p.lft and p.rgt');
$tree->setBaseQuery($query);
$hierarchy = $tree->fetchTree();
$tree->resetBaseQuery();
Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.