ping Datenbank


ping Datenbank

Pingplot war als Vorbereitung für ein etwas größeres Projekt gedacht, für das ich eine Datenbank einsetzen möchte. Also sollte ich erst mal lernen, wie man mit Datenbanken arbeitet.

Tatsächlich habe ich am Ende etwas mehr gelernt als gedacht und ein bisschen älteres Wissen aufgefrischt.

Grundidee

Daten, die laufend generiert werden sollen gesammelt und visualisiert werden. Zum Üben nehme ich den Ping zu einer Website. Der eignet sich weil es ein beständiger Strom von nicht zufälligen, aber auch nicht voraussagbaren Datenpunkten ist.

Es wird 4 zentrale Programme in unserem Setup geben:

  1. Die Steuerzentrale
  2. Die Datenquelle
  3. Die Datenbank
  4. Die Datenaufbereitung

Mir ist wichtig, das am Ende alles auf Knopfdruck funktioniert. Der Befehl, einen Plot zu erstellen ist nur ein Befehl sein und es ist keine weiteres Eingreifen meinerseits notwendig, erst recht kein hin- und herkopieren von Daten. Dieses Prinzip hat sich bei meiner Bachelorarbeit extrem gut bewährt - irgendwann drückt man nur noch auf den Knopf und hat etwas später 50 Graphen und Diagramme, die man analysieren kann.

Die Steuerzentrale - Gnu Make

Als ich zum ersten mal ein Makefile benutzt habe war ich wirklich begeistert von der Idee: Wenn man in einem Directory ohnehin immer wieder das gleiche macht, kann man das auch automatisieren. Und tatsächlich habe ich beim Programmieren immer ein Terminal laufen, in das ich permanent die selben Sachen eingebe:

Stattdessen schreibt man ein Makefile, indem das alles vorformuliert ist und gibt dann nur noch Befehle wie:

make compile

oder

make plot

Dank tab-completion geht das schneller und man vertippt sich nicht ausversehen. Ich finde es ja allgemein ganz gut, wenn ein Programm möglichst viel über sich selbst weiß und sich selbst quasi selbst organisiert (Solange klar ist, das ich noch der Chef bin - die Apple-Lösung mit diesen Mediathekformat, in das man nicht reinschauen kann).

Die Syntax von Makefiles ist erstmal sehr einfach. Man schreibt das target auf, einen Doppelpunkt dahinter - und danach eingerückt die Befehle, die ausgeführt werden sollen wenn das target aufgerufen wird.

plot:
	gnuplot graph.plot
	feh --reload 1 ping.png &

Mehr muss man erstmal nicht wissen.

Die Datenquelle - ein kurzes Shell-Skript

Um den Ping zu einer Website herauszufinden, reicht ein einfaches

$ ping www.schauderbasis.de
PING www.schauderbasis.de (5.45.107.67) 56(84) bytes of data.
64 bytes from v22013121188416155.yourvserver.net (5.45.107.67): icmp_seq=1 ttl=61 time=29.9 ms
64 bytes from v22013121188416155.yourvserver.net (5.45.107.67): icmp_seq=2 ttl=61 time=37.2 ms
64 bytes from v22013121188416155.yourvserver.net (5.45.107.67): icmp_seq=3 ttl=61 time=28.2 ms
...

Uns interessieren die Werte zwischen time= und ms. Um die herauszubekommen hat für mich folgendes funktioniert:

$ ping -c 1 www.schauderbasis.de
PING www.schauderbasis.de (5.45.107.67) 56(84) bytes of data.
64 bytes from v22013121188416155.yourvserver.net (5.45.107.67): icmp_seq=1 ttl=61 time=29.8 ms

--- www.schauderbasis.de ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 29.865/29.865/29.865/0.000 ms
    
$ ping -c 1 www.schauderbasis.de | sed -n 2p
64 bytes from v22013121188416155.yourvserver.net (5.45.107.67): icmp_seq=1 ttl=61 time=28.9 ms
$ ping -c 1 www.schauderbasis.de | sed -n 2p | awk '{print $8}'
time=29.1
$ ping -c 1 www.schauderbasis.de | sed -n 2p | awk '{print $8}' | cut -c 6-
29.6

Wunderbar.

Mehr Werte

Damit wir nachher eine ordentliche Datenbasis haben, wollen wir viele Werte hintereinander generieren. Eigentlich macht das ping ja schon selbst, aber wir bauen uns hier eine eigene Schleife, so das wir Daten sofort einlesen können.

Optionale Argumente (wusste ich vorher auch nicht) gehen so:

# number of datapoints to generate: take first argument or 10 as default
n=${1:-10}
# sleeptime: take second argument or 1 as default
t=${2:-1}

Am Ende (mit ein bisschen Zeug aus dem nächsten Abschnitt) sieht das ganze so aus.

database_name="pingDB"
table_name="pingtimes"
url="www.schauderbasis.de"
dbdo="mysql -u root -s $database_name -e"

# number of datapoints to generate: take first argument or 10 as
# default
n=${1:-10}
# sleeptime; take second argument or 1 as default
t=${2:-1}

for i in `seq $n`
do
    pingtime=$(ping -c 1 $url | sed -n 2p | awk '{print $8}' | cut -c 6-)
    $dbdo "insert into $table_name (Zeitpunkt, URL, Ping) values (NOW(), '$url', $pingtime);"
    sleep $t
done

Gar nicht mal so hässlich, von bash bin ich schlimmeres gewohnt.

Die Datenbank - MariaDB

Es gibt verschiedene Datenbanken für verschiedene Zwecke. Ich habe mich für MariaDB entschieden, hauptsächlich wegen dem Artikel im Arch-Wiki zum aufsetzen und dem Tutorial auf der Website von MariaDB, das mit genau so viel Information gegeben hat wie ich als blutiger Anfänger brauchte.

Beim Lernen hat mir wirklich sehr geholfen, das ich mit einem Makefile arbeite. So konnte ich einfach Zeilen wie diese eintragen:

database_name = pingDB
table_name = pingtimes
general_do = mysql -u root -e
dbdo = mysql -u root $(database_name) -e

prepare_database:
	$(general_do) "create database if not exists $(database_name)"

prepare_table:
	$(dbdo) "create table if not exists $(table_name) (Zeitpunkt TIMESTAMP, URL VARCHAR(30), Ping FLOAT UNSIGNED)";

show_table:
	$(dbdo) "select * from $(table_name)"

Was man sich aufgeschrieben hat, kann man schon mal nicht wieder vergessen.

Tatsächlich ist die SQL-Syntax gar nicht so schlimm, solange man relativ einfache Anfragen stellt. Das war bei mir zum Glück der Fall und das bisschen was ich brauchte konnte ich dann auch relativ flott auswendig.

Am Ende hatte ich eine Datenbank mit einer Tabelle, die (mit dem Skript von oben) so aussah:

$ make show_table
mysql -u root pingDB -e "select * from pingtimes"
+---------------------+----------------------+------+
| Zeitpunkt           | URL                  | Ping |
+---------------------+----------------------+------+
| 2016-01-25 16:56:40 | www.schauderbasis.de | 29.8 |
| 2016-01-25 16:56:41 | www.schauderbasis.de | 30.1 |
| 2016-01-25 16:56:42 | www.schauderbasis.de | 29.0 |
| 2016-01-25 16:56:43 | www.schauderbasis.de | 32.2 |
| 2016-01-25 16:56:44 | www.schauderbasis.de | 28.8 |
| 2016-01-25 16:56:45 | www.schauderbasis.de | 29.6 |
| 2016-01-25 16:56:47 | www.schauderbasis.de | 30.1 |
| 2016-01-25 16:56:48 | www.schauderbasis.de | 29.8 |
| 2016-01-25 16:56:49 | www.schauderbasis.de | 28.6 |
| 2016-01-25 16:56:50 | www.schauderbasis.de | 29.2 |
+---------------------+----------------------+------+

Die Datenaufbereitung - Gnuplot

Eigentlich kam für mich kein anderes Tool in Frage, Gnuplot passt einfach zu gut. Ich habe schonmal was zu verschiedenen Plottern aufgeschrieben und hier war die Entscheidung klar.

Das schwierigste war die Frage, wie man die Daten aus der Datenbank in Gnuplot hinein bekommt. Gut das Gnuplot alles kann:

# Output from mysql is normaly formated as ascii-boxes,
# with the flag -B it is just tab-separated.
set datafile separator "\t"

plot '< mysql -u root -B pingDB -e "SELECT Zeitpunkt, Ping FROM pingtimes;"' using 1:2

Im Prinzip wird hier die SQL-Abfrage direkt von Gnuplot ausgeführt. Kein Problem.

Es ist gar nicht so klar, wie das mit der Zeit eingelesen werden soll. SQL liefert das Datum und die Uhrzeit schön nach ISO 8601: 2016-01-25 16:56:40

Gnuplot kommt von klugen Leuten, die wissen dass es auf der Welt sehr viele sehr schlimme Formate gibt, in der Leute die Zeit angeben. Deswegen gibt man einfach an, in welchem Format das Datum eingelesen und ausgegeben werden soll:

# time format used for reading input
set xdata time
set timefmt "%Y-%m-%d %H:%M:%S"

# time format used for printing on axis
set format x "%H:%M:%S"

Einfacher geht es nicht. Eine Aufschlüsselung der Variablen (falls nötig) gibt es im hervorragenden Handbuch. Es scheint aber das gleiche Format zu sein wie bei dem Programm date, also reicht wsl auch die entsprechende manpage.

Jetzt ist nur noch die Frage, wie der Graph aussehen soll. Ich habe mich entschieden, die Werte interpolieren zu lassen, damit der Graph schön glatt ist. Das Stichwort hier heißt smooth, man sollte es in Gedanken aber immer smooooooooth aussprechen.

Und das wars

Eigentlich ziemlich einfach, hier funktionieren ein paar mächtige Werkzeuge sehr gut zusammen. Es hat Spaß gemacht und ich habe jede Menge über wichtige Standardwerkzeuge gelernt.

Der Code liegt hier zur freien Verfügung (mit freier Lizenz natürlich).

Gedanken zum Projekt