Kapitel 10. Asterisk-Datenbank (AstDB)

1. Einleitung

Das Problem bei der Benutzung von Variablen im Dialplan ist, dass der Wert dieser und überhaupt aller zur Laufzeit definierten Variablen bei einem Systemabsturz oder einem Neustart von Asterisk gelöscht bzw. auf ihre Anfangswerte zurückgesetzt werden. Dadurch sind bestimmte Einsatzszenarien gar nicht denkbar. Wenn man beispielweise eine Call-Forwarding-Funktion[108] oder ein Calling-Card-System implementieren möchte, dann sollten diese natürlich z.B. das Restguthaben in einer Datenbank speichern, damit diese Daten bei einem Neustart des Systems wieder korrekt zur Verfügung stehen.

Performance

Bezüglich der Asterisk-Datenbank wird immer wieder die Frage nach der Performance derselben gestellt. Das lässt sich nicht so pauschal beantworten. Falls Sie nur kleine Datenbestände (wie unser Wahlwiederholungsbeispiel) benötigen, so ist die Asterisk-Datenbank sicherlich eine sinnvolle Wahl. Bei größeren und komplexen Datenbeständen sollten Sie aber überlegen, ob das Verwenden einer externen SQL-Datenbank geeigneter ist. Allerdings ist diese Diskussion bei dem überwiegenden Teil aller Anwendungen rein theoretischer Natur, da es sich bei der Asterisk-Datenbank um eine Berkley DB handelt und diese ausreichend performant ist. Tatsächlich ist die Berkley DB, wenn es sich um reine "Key => Value"-Datenpaare handelt, mit die schnellste ihrer Zunft. Sie sollten sich daher diese Frage erst stellen, wenn die Geschwindigkeit der Datenbank nachweislich zu Problemen führt oder Sie eine größere Installation mit umfangreicher Funktionalität aufsetzen wollen.

2. Asterisk-Datenbank

Asterisk bringt standardmäßig eine Datenbank mit, die die Berkeley DB (BDB)[109] als Datenbank-Engine einsetzt. In dieser Datenbank-Engine wird immer einem Schlüssel (key) ein Wert (value) zugeordnet, wobei Schlüssel in Familien (family) zusammengefasst werden.

[Warnung]Warnung

Bis einschließlich Asterisk Version 1.2 wurden die folgenden Befehle eingesetzt:

  • DBput(family/key=value)

    zum Speichern eines Werts in der Datenbank.

  • DBget(var=family/key)

    um einen Wert aus der Datenbank auszulesen. Wird der aufgerufene Key in der Datenbank nicht gefunden, erhöht sich die Priorität um 101.

Diese Befehle werden ab der Version 1.4 nicht mehr unterstützt. Aus diesem Grund wird hier nur die neue Variante über die Funktion DB() besprochen. Diese Variante funktioniert auch in der Version 1.2.

Auf diese Datenbank kann mit der Funktion DB() zugegriffen werden (siehe DB()).

Werte in die Datenbank schreiben

Die Funktion DB() kann innerhalb der Applikation Set() aufgerufen werden. Soll in der Family obst der Eintrag apfel den Wert 20 bekommen, so geschieht das mit folgendem Aufruf:

exten => 1234,1,Set(DB(obst/apfel)=20)

Werte aus der Datenbank lesen

Die Werte in der Datenbank können mit der Funktion DB() in der Form ${DB(family/key)} abgerufen werden. Um den Inhalt des Eintrags apfel in der Family obst auf dem Command Line Interface auszugeben, kann man folgenden Aufruf ausführen:

exten => 1234,1,NoOp(obst/apfel hat den Wert ${DB(obst/apfel)})

Soll der Inhalt dieses Datenbankfeldes in der Variablen apfelmenge gespeichert werden, so kann dies mit Set() erfolgen:

exten => 1234,1,Set(apfelmenge=${DB(obst/apfel)})

Werte aus der Datenbank löschen

Die beiden Applikationen DBdel() und DBdeltree() können zum Löschen von Datenbankeinträgen genutzt werden.

DBdel() und ${DB_DELETE()}

In der Asterisk Version 1.2 löscht DBdel() einen einzelnen Eintrag in der Datenbank. Soll der Key apfel in der Family obst gelöscht werden, so geschieht das wie folgt:

exten => 1234,1,DBdel(obst/apfel)

In der Asterisk Version 1.4 ist DBdel() deprecated und man soll die Funktion ${DB_DELETE()} benutzen. Der Aufruf kann wie folgt erfolgen:

exten => 1234,1,NoOp(${DB_DELETE(obst/apfel)})

Siehe auch DBdel() und DB_DELETE().

DBdeltree()

Soll eine ganze Family gelöscht werden, so verwendet man DBdeltree(). Die Family obst kann mit

exten => 1234,1,DBdeltree(obst)

gelöscht werden.

Siehe auch: DBdeltree()

3. Datenbankzugriff vom CLI

Vom Command Line Interface (CLI) kann der Administrator ebenfalls auf die Datenbank zugreifen.

[Tipp]Tipp

Sollte man den entsprechenden Befehl oder die genaue Syntax vergessen haben, so kann man jederzeit help database im CLI eingeben:

big-island*CLI> help database
             database del  Removes database key/value
         database deltree  Removes database keytree/values
             database get  Gets database value
             database put  Adds/updates database value
            database show  Shows database contents
         database showkey  Shows database contents
[Wichtig]Wichtig

Denken Sie bei den folgenden Beschreibungen daran, dass Sie, wenn die Family, der Schlüssel oder der Wert Leerzeichen enthält, Anführungszeichen (") um den String schreiben müssen! Also z.B. database put test eintrag "Hallo Welt".

Werte in die Datenbank schreiben

Mit database put family key value können Werte in die Datenbank geschrieben werden:

big-island*CLI> database put obst apfel 20
Updated database successfully

Werte aus der Datenbank lesen

Mit database get family key lesen Sie Werte aus der Datenbank aus:

big-island*CLI> database get obst apfel
Value: 20d*CLI>

Werte aus der Datenbank löschen

database del family key und database deltree family löschen Werte aus der Datenbank.

database del

Um den Key apfel aus der Family obst zu löschen, kann man database del aufrufen:

big-island*CLI> database del obst apfel
Database entry removed.

database deltree

Mit database deltree kann eine ganze Family aus der Datenbank gelöscht werden:

big-island*CLI> database deltree obst
Database entries removed.

Datenbankinhalt anzeigen

Mit den Befehlen database show und database showkey kann der Administrator den Inhalt der Datenbank auf dem CLI ausgeben lassen. Ein Beispiel:

big-island*CLI> database put einkaufsliste eier 2
Updated database successfully
big-island*CLI> database put einkaufsliste butter 250
Updated database successfully
big-island*CLI> database put einkaufsliste zucker 500
Updated database successfully
big-island*CLI> database show
/einkaufsliste/butter                             : 250                      
/einkaufsliste/eier                               : 2                        
/einkaufsliste/zucker                             : 500  
big-island*CLI> database showkey butter
/einkaufsliste/butter                             : 250
big-island*CLI> database deltree einkaufsliste
Database entries removed.

4. Datenbankzugriff von der Shell

Über den Befehl asterisk -rx 'command' können natürlich alle CLI-Kommandos auch von einem Shell-Skript ausgeführt werden. Das folgende Beispiel zeigt, wie man von der Shell aus die Datenbank erst mit Werten füllt, diese dann ausliest und zum Schluss wieder löscht:

big-island:~# asterisk -rx 'database put test var1 23'
Updated database successfully
big-island:~# asterisk -rx 'database put test var2 42'
Updated database successfully
big-island:~# asterisk -rx 'database show test'
/test/var1                                        : 23                       
/test/var2                                        : 42                       
big-island:~# asterisk -rx 'database get test var2'
Value: 42
big-island:~# asterisk -rx 'database deltree test'
Database entries removed.
[Wichtig]Wichtig

Denken Sie wieder daran, dass Sie, wenn die Family, der Schlüssel oder der Wert Leerzeichen enthält, Anführungszeichen (") um den String schreiben müssen! Also z.B.

big-island:~# asterisk -rx 'database put test eintrag "Hallo Welt"'

5. Backup der Datenbank

Die Asterisk-Datenbank wird standardmäßig im Verzeichnis /var/lib/asterisk/astdb/ gespeichert. Sobald Asterisk gestoppt ist ist, kann man das Verzeichnis sichern.

Ein Backup im laufenden Betrieb kann auch von der Shell durch folgenden Befehl

asterisk -rx "database show" > /tmp/backup-asterisk-database.txt

durchgeführt werden. Allerdings ist das Restore dann etwas aufwendiger.

6. Anwendungsbeispiel CallForwarding

Wer bei Abwesenheit alle Gespräche an seine Durchwahl z.B. an sein Handy weiterleiten will, benötigt eine CallForwarding-Funktionalität. Diese kann entweder vom Endgerät (also dem SIP-Telefon) oder zentral von der Telefonanlage realisiert werden. Letztere Variante ist in der Praxis vorzuziehen, da man dadurch unabhängig von den Endgeräten ist (diese also im Zweifelsfall auch austauschen kann) und zusätzlich die gesamte Kontrolle auf dem Server behält. Außerdem ist im Falle eines Stromausfalls die Konfiguration nicht verloren und wird beim nächsten Start der Telefonanlage automatisch wiederhergestellt.

Einfaches CallForwarding

Bei der Apfelmus GmbH soll jeder Mitarbeiter die Möglichkeit bekommen, Gespräche zu seinem Telefon an eine beliebige andere Nummer weiterzuleiten. Um diese Weiterleitung zu aktivieren, muss die interne Rufnummer 44 gefolgt von der Zielrufnummer angerufen werden. Zum Deaktivieren der Weiterleitung muss wieder die 44 (aber diesmal ohne eine weitere Nummer) angerufen werden. Dies wird mit folgendem Wählplan realisiert:

[from-intern]
; Call Forwarding für einen einzelnen Anschluss
;
; aktivieren
exten => _44X.,1,Answer()
exten => _44X.,n,Set(DB(CF/${CALLERID(num)})=${EXTEN:2})
exten => _44X.,n,SayDigits(${EXTEN:2})
exten => _44X.,n,NoOp(Weiterleitung fuer ${CALLERID(num)} auf ${EXTEN:2} aktiviert.) 
exten => _44X.,n,Hangup()

; deaktivieren
exten => 44,1,Answer()
exten => 44,n,DBdel(CF/${CALLERID(num)})
exten => 44,n,Playback(auth-thankyou)
exten => 44,n,NoOp(Weiterleitung fuer ${CALLERID(num)} deaktiviert.) 
exten => 44,n,Hangup()

[from-extern]
exten => _X.,1,NoOp(Anruf von ${CALLERID(num)} fuer ${EXTEN})
exten => _X.,n,GotoIf($[foo${DB(CF/${EXTEN})} != foo]?normal:forward)
exten => _X.,n(normal),Dial(SIP/${EXTEN})
exten => _X.,n(forward),NoOp(Anruf fuer ${EXTEN} wird verbunden zu ${DB(CF/${EXTEN})})
exten => _X.,n,Dial(local/${DB(CF/${EXTEN})})

Komplexes CallForwarding

Diesmal soll in der Apfelmus GmbH jeder Mitarbeiter ein CallForwarding aktivieren können, allerdings soll es ein weiteres CallForwarding für die gesamte Firma geben, damit bei einem Betriebsausflug alle Gespräche an eine andere Niederlassung geleitet werden können. Diese Weiterleitung wird über die Funktionsnummer 55 aktiviert (gefolgt von der Zielrufnummer). Die große Weiterleitung für die gesamte Anlage hat dabei eine höhere Priorität als die einzelnen Regeln. Eine Realisierung kann wie folgt aussehen:

[from-intern]
; CallForwarding für einen einzelnen Anschluss
;
; aktivieren
exten => _44X.,1,Answer()
exten => _44X.,n,Set(DB(CF/${CALLERID(num)})=${EXTEN:2})
exten => _44X.,n,SayDigits(${EXTEN:2})
exten => _44X.,n,NoOp(Weiterleitung fuer ${CALLERID(num)} auf ${EXTEN:2} aktiviert.) 
exten => _44X.,n,Hangup()

; deaktivieren
exten => 44,1,Answer()
exten => 44,n,DBdel(CF/${CALLERID(num)})
exten => 44,n,Playback(auth-thankyou)
exten => 44,n,NoOp(Weiterleitung fuer ${CALLERID(num)} deaktiviert.) 
exten => 44,n,Hangup()

; CallForwarding für die gesamte Telefonanlage
;
; aktivieren
exten => _55X.,1,Answer()
exten => _55X.,n,Set(DB(CF/anlage)=${EXTEN:2})
exten => _55X.,n,SayDigits(${EXTEN:2})
exten => _55X.,n,NoOp(Weiterleitung der Anlage auf ${EXTEN:2} aktiviert.) 
exten => _55X.,n,Hangup()

; deaktivieren
exten => 55,1,Answer()
exten => 55,n,DBdel(CF/anlage)
exten => 55,n,Playback(auth-thankyou)
exten => 55,n,NoOp(Weiterleitung der Anlage deaktiviert.) 
exten => 55,n,Hangup()

[from-extern]
exten => _X.,1,NoOp(Anruf von ${CALLERID(num)} fuer ${EXTEN})
exten => _X.,n,GotoIf($[foo${DB(CF/anlage)} != foo]?cfanlage:zweiteregel)
exten => _X.,n(zweiteregel),GotoIf($[foo${DB(CF/${EXTEN})} != foo]?cfnormal:normal)
exten => _X.,n(normal),Dial(SIP/${EXTEN})
exten => _X.,n(cfnormal),NoOp(Anruf fuer ${EXTEN} wird verbunden zu ${DB(CF/${EXTEN})})
exten => _X.,n,Dial(local/${DB(CF/${EXTEN})})
exten => _X.,n(cfanlage),NoOp(Anruf fuer ${EXTEN} wird verbunden zu ${DB(CF/anlage)})
exten => _X.,n,Dial(local/${DB(CF/anlage)})

7. Anwendungsbeispiel CallingCard

In der Apfelmus GmbH sollen private Gespräche über eine CallingCard abgerechnet werden. Diese wird vom Hausmeister über sein Telefon virtuell aufgeladen (durch Anwahl der internen Servicenummer 88 gefolgt von der 3-stelligen Durchwahl und vom gewünschten Geldbetrag in Euro).[110] Die Gespräche werden zu einem Pauschalpreis von 1 Euro pro Gespräch abgerechnet. Diese privaten Gespräche werden über die interne Servicenummer 99 gefolgt von der Zielrufnummer geführt. Dabei ist zu beachten, dass bei einem Besetzt (busy) kein Geldbetrag vom Konto abgebucht wird. Die interne Rufnummer 98 kann dazu benutzt werden, den aktuellen Betrag auf dem CallingCard-Konto abzurufen.

[from-hausmeister]
; Aufladen der virtuellen CallingCard
;
exten => _88XXX.,1,Answer()
exten => _88XXX.,2,Set(DB(CallingCard/${EXTEN:2:3})=${EXTEN:5})
exten => _88XXX.,3,SayNumber(${EXTEN:5})
exten => _88XXX.,4,NoOp(CallingCard fuer ${EXTEN:2:3} mit ${EXTEN:5} aufgeladen.)
exten => _88XXX.,5,Hangup()

[from-intern]
; private Gespraeche
;
exten => _99.,1,GotoIf($[${DB(CallingCard/${CALLERID(num)} > 0]?2:200)
exten => _99.,2,Set(DB(CallingCard/${CALLERID(num)})=$[DB(CallingCard/${CALLERID(num)}) - 1])
exten => _99.,3,Dial(local/${EXTEN:2})
exten => _99.,104,Set(DB(CallingCard/${CALLERID(num)})=$[DB(CallingCard/${CALLERID(num)}) + 1])

exten => _99.,200,NoOp(CallingCard Konto ${CALLERID(num) ist leer.)
exten => _99.,201,Answer()
exten => _99.,202,SayNumber(0)
exten => _99.,203,Hangup()

; Mit der 98 kann der aktuelle Kontostand abgefragt werden.
;
exten => 98,1,Answer()
exten => 98,2,SayNumber(${DB(CallingCard/${CALLERID(num)})})
exten => 98,3,Hangup()


[108] CallForwarding-Funktionalität: Jeder Teilnehmer kann durch Wahl einer bestimmten Nummer alle Gespräche an einen anderen Apparat weiterleiten lassen. Durch Wahl einer anderen Nummer wird diese Funktion wieder deaktiviert.

[109] Die Berkeley-Datenbank (Berkeley DB) ist eine hochperformante, eingebettete Datenbank-Bibliothek mit Programmierschnittstellen zu C, C++, Java, Perl, Python, Tcl und vielen weiteren Programmiersprachen. Die DB speichert beliebige Schlüssel- oder Datenpaare und unterstützt mehrere Datenelemente für einen einzelnen Schlüssel. Die DB ermöglicht tausende von simultanen Threads zum Manipulieren der Datenbanken, die bis zu 256 Terabyte groß sein können, und läuft auf einer großen Anzahl von Systemen, unter anderem auf den meisten UNIX-artigen und Windows-Systemen und auch auf Echtzeitbetriebssystemen. [Zitiert aus http://de.wikipedia.org/wiki/Berkeley_DB]

[110] Um das Beispiel möglichst einfach zu halten, wird der Fall, dass auf ein bereits bestehendes CallingCard-Konto ein weiterer Betrag eingezahlt wird (also eine Addition vollzogen werden muss) nicht besprochen. Ebenfalls nicht behandelt wird die Möglichkeit, dass ein Mitarbeiter von einem anderen Telefon ein privates Gespräch führen könnte.


Version 1.2, November 2002

Neue Version verfügbar

Sie betrachten gerade die alte Version des Buches (Version 1.0). Wir empfehlen Ihnen für Asterisk 1.4 und 1.6 die neue Version des Buches.

Asterisk-Tag 2008

Lernen Sie Mark Spencer (den Erfinder von Asterisk) kennen! Viele Vorträge, Case-Studies und Workshops rund um das Thema VoIP. Asterisk-Tag.org

Das gedruckte Buch

Werbung