Nachdem ich einige Zeit und viele Suchen nach klieneren oder größeren Hilfestellungen benötigt habe, um den Raspberry als Firebird-Server inkl. Möglichekeiten zum Backup/Restore der Datenbank von einem Windows-PC aus nutzen zu können, möchte ich hier meine Erfahrungen dazu weitergeben.
Voraussetzung:
Die Grundinstallation mit Raspbian ist bereits erfolgt, dazu gibt es zahlreiche Anleitungen in diesem und anderen Foren.
Wir starten mit der Installation des Firebird-Servers. Verwendet wurde die Version 2.5 in der Superserver-Ausführung.
Ich gehe hier davon aus, dass bereits eine Datenbank als .fdb-Datei vorliegt, die nur noch übertragen werden muss. Die Erzeugung einer neuer Firebird-Datebank ist hinreichend in anderen Foren (Firebird) beschrieben und nicht Gegenstand dieser Anleitung.
Wir erstellen nun ein Verzeichnis, in das die Datenbank kopiert werden kann. Ich habe es unterhalb des Home-Ordners des Users pi erstellt. Wer dazu gerne einen eigenen User verwenden möchte findet entsprechende Anleitungen zur Erstellung des Users und des Home-Verzeichnisses.
Damit der Firebird-Server später darauf auch zugreifen kann, tragen wir als Gruppe pi und als Owner firebid ein und setzen entsprechende Berechtigungen (770).
Nun kopieren wir die Datenbank in diesen Ordner (z.B. mit Hilfe von WinSCP oder Filezilla) und ändern auch deren Gruppe/Owner und Rechte.
Da wir den Server von extern ansprechen wollen (keine auf dem Raspberry laufende PHP-Applikation sondern eine Windows-Applikation auf externen Clients) müssen wir dem Firebird-Service erlauben, nicht nur auf localhost zu lauschen.
Wir ändern die Einstellung in \etc\firebird\2.5\firebird.conf. Dort suchen wir nach RemoteBindAddress und kommentieren diese Zeile durch ein vorangestelltes # aus (danach habe ich Stunden gesucht und schon Firewalls/Router im Verdacht gehabt, weil ich den Service einfach nicht von außen erreichen konnte - und dann war das sooo einfach
ZitatAlles anzeigen# Allows incoming connections to be bound to the IP address of a
# specific network card. It enables rejection of incoming connections
# through any other network interface except this one. By default,
# connections from any available network interface are allowed.
# If you are using Classic Server, this setting is for Windows only.
# Under Linux, BSD or Mac OS X, with Classic server use xinetd or launchd
# configuration file (bind parameter).
#
# Type: string
#
#RemoteBindAddress = localhost
Alternativ kann hier auch angegeben werden, welche Clients zugreifen dürfen, hier öffnen wir den Port für alle Adressen von außen!
ACHTUNG: das ist OK in einem begrenzten internen LAN, sollte für einen Raspi der ggf. auch vom Internet aus erreichbar ist aber nicht ganz so offen konfiguriert werden!
Wenn wir schon dabei sind tragen wir unsere Datenbank auch gleich noch in der aliases.conf ein, damit wir von außen nicht wissen müssen, in welchem Ordner die DB liegt, sondern einfach den Alias testdb verwenden können.
Zitattestdb=/home/pi/firebird/testdb.fdb
Jetzt ist ein erster Test sinnvoll, ob unsere DB zumindet vom Raspi aus angesprochen werden kann.
Wenn alles funktioniert meldet sich firebird mit
ZitatDatabase: testdb, user sysdba
wir beenden den Test mit
Wer nur den Zugriff auf die Firebird-Datenbank von einem externen Client aus benötigt, ist damit schon fertig. Die Datenbank kann nun aus gängigen Tools wie z.B. IBExpert, Marathon oder (mein bevorzugtes Tool) der "Database Workbench Lite" wie jede andere Datenbank angesprochen werden.
In der Konfiguration des jeweiligen DB-Tools gibt man nun folgende Daten ein:
Host: hostname des Raspberry, also z.B. raspbian
Protocol: TCP/IP
In der Datenbankkonfiguration verwenden wir den Alias testdb und zunächst sysdba und masterkey für die Authentifikation.Empfehlenswert ist nun die Anlage eines zusätzlichen Datenbank-Users und die Vergabe von Rechten (Grant) an diesen User. Dies kann z.B. mit der kostenlosen "Database Workbench Lite" in deren Usermanager und Grant-Manager erfolgen (bietet z.B. die Option Grant ALL on ALL), notfalls auch per isql-fb für jedes einzelne Datenbak-Objekt.
Die nächsten Schritte sind nur notwendig, wenn man zusätzliche Komfort-Funktionen für die Anwender der Windows-Datenbankapplikation bereitstellen möchte.
Ich wollte gerne eine einfache Möglichkeit bieten, eine Datensicherung und ein Restore auf lokal am Windows-PC angeschlossene Speichermedien zu ermöglichen. Dies sollte aus der Windows-Applikation heraus möglich sein. Dazu habe ich zunächst geplant, die Dateiverwaltung über FTP oder SFTP zu realisieren, habe aber in Delphi dazu keine so richtig elegante Methode gefunden. Da es nur um eine einzige zu kopierende Datei geht (die als .FBK erzeugte Sicherheitskopie) habe ich mich schließlich dafür entschieden, auf dem Raspberry eine SAMBA-Freigabe einzurichten.
Dabei gilt folgender einfacher Aufbau:
- Die Datenbank liegt in /home/pi/firebird
- Die Sicherheitskopie wird in /home/pi/firebird/backup erzeugt und von dort für ein Restore auch wieder gelesen.
Den Ordner erstellen wir mit den gewünschten Rehcten (hier Vollzugriff für alle):
Wir benötigen nun noch eine Freigabe für diesen einen Ordner und beginnen mit der Samba-Installation. Eine detaillierte Anleitung dazu findet Ihr z.B. unter http://jankarres.de/2013/11/raspbe…r-installieren/. Hier nur die notwendigsten Schritte daraus:
Danach fügen wir folgende Daten ein:
Zitat[FB_Backup]
path = /home/pi/firebird/backup
writable=yes
guest ok=yes
Danach den Samba-Service mit den neuen Einstellungen aktivieren
oder auch mal die EInstellungen prüfen
Nun sollte auf einem Windows-Client bereits die Freigabe auftauchen und wie gewohnt verwendet werden können. Der Rest des Artikels beschäftigt sich nun mit der Programmierung in Delphi.
Ich verwende hier die Komponenten UIBBackup und UIBRestore nach folgendem Muster:
Backup:
- erzeuge ein Backup in /home/pi/firebird/backup
- kopiere diese Datei über die SAMBA-Freigabe lokal auf den Windows-Client an einen vom Anwender ausgewählten Ort (z.B. einen USB-Stick, eine interne Festplatte, ein anderes Netzlaufwerk etc.)
Restore:
- kopiere eine vom Anwender ausgewählte Datensicherung über die SAMBA-Freigabe nach /home/pi/firebird/backup
- lese die Datensicherung in die Firebird-Datenbank zurück.
Die Zugangsdaten für die Datenbank stecken in entsprechenden Variablen:
- servertype: embedded für eine Einzelplatzinstallation ohne Server, network für eine Installation mit Server (nur um die geht es hier)
- Hostname: im Artikel verwende ich den Dafault raspbian
- dbname: Name der Datenbank, hier wie im obigen Beispiel testdb
- sharedir: Freigabe wie sie aus Sicht des Windows-Clients aus sichtbar ist
Zunächst das Backup.
Dazu habe ich im Datenmodul u.A. eine TStringlist angelegt, die von der Komponente gefüllt wird.
// Callback-Prozedur für die Backup-Komponente, Eintragen des Backup-Protokolls
procedure DataMod.UIBBackup1Verbose(Sender: TObject; Message: string);
begin
slProtokoll.Add(Message);
end;
procedure DataMod.Backup(Zielordner, Serverordner: String; daily: boolean);
// Zielordner: lokaler Ordner wie vom Anwender ausgewählt
// Serverordner: Ordner aus Sicht des DB-Servers, hier also /pi/home/firebird/backup
var
wjahr, wmonat, wtag: word;
shareDir, localDir: String;
begin
with DataMod do
begin
CloseDB; // --> alle ggf offenen Queries schließen
ZConnection1.Disconnect; --> Verbindung zur Dtenbank beenden, da nur eine geschlossene DB gesichert werden kann. Wir prüfen hier aber nicht, ob nmoch andere Clients aktiv sind.
// Datenbankparameter setzen
UIBBackup1.Database := DBName; // beim openDB gespeicherte Werte verwenden
UIBBackup1.Host := Hostname; // beim openDB gespeicherte Werte verwenden
if ServerType = 'embedded' then
begin
if serverordner='' then ServerOrdner := ExtractFilePath(DBName);
UIBBackup1.Protocol:= proLocalHost --> wichtig: das richtige Protokoll verwenden!
end
else
UIBBackup1.Protocol:= proTCPIP; --> wichtig: das richtige Protokoll verwenden!
if (length(Serverordner) > 0) or (daily=true) then
begin
// Dateiname aus Datum erzeugen
DecodeDate(date, wjahr, wmonat, wtag);
Serverordner := Serverordner +Format('%0.4d', [wjahr]) + Format('%0.2d', [wmonat]) + Format('%0.2d', [wtag]) + '_testdb.FBK';
end;
UIBBackup1.Backupfiles.Clear;
UIBBackup1.Backupfiles.Add(Serverordner); //--> der Komponente sagen wohin sie sichern soll
// Für Backup und Restore mit entsprechenden Rechten anmelden, also entweder als sysdba oder vorher dem verwendeten User die Rechte geben
UIBBackup1.username := 'sysdba';
UIBBackup1.Password := 'masterkey';
try
UIBBackup1.run; // Backup starten
// erzeugte Sicherung auf den Client holen
localDir :=Zielordner;
// Dateiname mit dem Ornder der Freigabe erzeugen
shareDir := shareDir +Format('%0.4d', [wjahr]) + Format('%0.2d', [wmonat]) + Format('%0.2d', [wtag]) + '_testdb.FBK';
localDir := localDir + Format('%0.4d', [wjahr]) + Format ('%0.2d', [wmonat]) + Format('%0.2d', [wtag]) + '_testdb.FBK';
// Datei kopieren wenn auf Server erzeugt, bei servertype embedded liegt sie bereits im Zielordner
if (ServerType = 'network') and (FileExists(shareDir)) then
copyFile(PChar(shareDir), PChar(localDir),false);
except
on E: Exception do
begin ;
slProtokoll.Add(E.Message); // im Fehlerfall verwenden wir das selbe Protokoll, das die Komponnete sonst füllen würde
showMessage(E.Message); // ...und zeigen den Fehler zusätzlich an
end;
end;
slProtokoll.SaveToFile(ExtractFilePath(GetTempDirectory) + 'backup.log');
slProtokoll.free;
OpenDB; // Datenbank wieder öffnen
end;
end;
end;
Alles anzeigen
Ganz ähnlich funktioiert das Restore:
procedure DataMod.Restore(Quellordner: String; BackupFilename: String);
begin
with dmBisam_SQL do
begin
if length(BackupFilename) > 0 then
with UIBRestore1 do
begin
// Datenbankparameter setzen
Database := DBName; // beim openDB gespeicherte Werte verwenden
Host := Hostname; // beim openDB gespeicherte Werte verwenden
if ServerType = 'network' then // passendes Protokoll eintragen
Protocol:= proTCPIP
else
Protocol:= proLocalHost;
ZConnection1.Disconnect;
Backupfiles.Clear;
Backupfiles.Add(Quellordner + BackupFilename);
// jetzt die Wiederherstellung im Original-Datenbankfile durchführen
try
username := 'sysdba';
Password := 'masterkey';
run;
slProtokoll.SaveToFile(GetTempDirectory+ 'restore.log');
showMessage(rsRestoreCompleted);
slProtokoll.free;
except
on E: Exception do
begin ;
slProtokoll.Add(E.Message);
slProtokoll.SaveToFile(GetTempDirectory + 'restore.log');
showMessage(E.Message);
end;
end;
end;
OpenDB;
openWindowsFile('restore.log', ExtractFilePath(GetTempDirectory));
end;
end;
Alles anzeigen
Die Auswahl der an die Procedures übergebenen Parameter wie Zielordner, Quellordner wird, soweit es die lokalen Ordner betrifft, vom Anwender vorher über die üblichen Dialog-Funktionen (selectDirctory) in Delphi durchgeführt.
Soweit eine erste Fassung dieses Tutorials. Anmerkungen und Verbesserungsvorschläge sind gerne willkommen !
Siegbert