# $Id$ # package main; use strict; use warnings; use feature qw/say switch/; use configDB; sub CommandConfigdb($$); my @pathname; sub configdb_Initialize($$) { my %hash = ( Fn => "CommandConfigdb", Hlp => "info|list|diff|uuid| reorg|recover|backup ,access additional functions from configDB" ); $cmds{configdb} = \%hash; } ##################################### sub _configdb_rM($$) { my ($modpath,$backupdir) = @_; my $msg; my $ret; if (!opendir(DH, $modpath)) { $msg = "Can't open $modpath: $!"; Log 1, "backup $msg"; return $msg; } my @files = <$modpath/*>; foreach my $file (@files) { if ($file eq $backupdir && (-d $file || -l $file)) { Log 4, "backup exclude: '$file'"; } else { Log 4, "backup include: '$file'"; push @pathname, $file; } } return $ret; } sub _configdb_cA($) { my $backupdir = shift; my $backupcmd = (!defined($attr{global}{backupcmd}) ? undef : $attr{global}{backupcmd}); my $symlink = (!defined($attr{global}{backupsymlink}) ? "no" : $attr{global}{backupsymlink}); my $tarOpts; my $msg; my $ret; my $dateTime = TimeNow(); $dateTime =~ s/ /_/g; $dateTime =~ s/(:|-)//g; my $cmd=""; if (!defined($backupcmd)) { if (lc($symlink) eq "no") { $tarOpts = "cf"; } else { $tarOpts = "chf"; } # prevents tar's output of "Removing leading /" and return total bytes of archive $cmd = "tar -$tarOpts - @pathname |gzip > $backupdir/FHEM-$dateTime.tar.gz"; } else { $cmd = "$backupcmd \"@pathname\""; } Log 2, "Backup with command: $cmd"; $ret = `($cmd) 2>&1`; if($ret) { chomp $ret; Log 1, "backup $ret"; } if (!defined($backupcmd) && -e "$backupdir/FHEM-$dateTime.tar.gz") { my $size = -s "$backupdir/FHEM-$dateTime.tar.gz"; $msg = "backup done: FHEM-$dateTime.tar.gz ($size Bytes)"; Log 1, $msg; $ret .= "\n".$msg; } return $ret; } sub _configdb_backup { my $modpath = $attr{global}{modpath}; my $msg; my $ret; # set backupdir my $backupdir; if (!defined($attr{global}{backupdir})) { $backupdir = "$modpath/backup"; } else { if ($attr{global}{backupdir} =~ m/^\/.*/) { $backupdir = $attr{global}{backupdir}; } elsif ($attr{global}{backupdir} =~ m/^\.+\/.*/) { $backupdir = "$modpath/$attr{global}{backupdir}"; } else { $backupdir = "$modpath/$attr{global}{backupdir}"; } } # create backupdir if not exists if (!-d $backupdir) { Log 4, "backup create backupdir: '$backupdir'"; $ret = `(mkdir -p $backupdir) 2>&1`; if ($ret) { chomp $ret; $msg = "backup: $ret"; return $msg; } } # get pathnames to archiv $ret = _configdb_rM($modpath,$backupdir); # create archiv $ret = _configdb_cA($backupdir); @pathname = []; undef @pathname; return $ret; } sub CommandConfigdb($$) { my ($cl, $param) = @_; my @a = split(/ /,$param); my ($cmd, $param1, $param2) = @a; $cmd = $cmd ? $cmd : ""; $param1 = $param1 ? $param1 : ""; $param2 = $param2 ? $param2 : ""; my $configfile = $attr{global}{configfile}; return "\n error: configDB not used!" unless($configfile eq 'configDB' || $cmd eq 'migrate'); my $ret; given ($cmd) { when ('attr') { Log3('configdb', 4, 'configdb: attr $param1 $param2 requested.'); if ($param1 eq "" && $param2 eq "") { # list attributes foreach my $c (sort keys %{$attr{configdb}}) { my $val = $attr{configdb}{$c}; $val =~ s/;/;;/g; $val =~ s/\n/\\\n/g; $ret .= "attr configdb $c $val"; } } elsif($param2 eq "") { # delete attribute undef($attr{configdb}{$param1}); $ret = " attribute $param1 deleted"; } else { # set attribute $attr{configdb}{$param1} = $param2; $ret = " attribute $param1 set to value $param2"; } } when ('backup') { if($^O =~ m/Win/) { Log3('configdb', 4, "configdb: error: backup requested on MS platform."); $ret = "\n error: backup not supported for Windows"; } else { Log3('configdb', 4, "configdb: backup requested."); $ret = _configdb_backup; } } when ('diff') { return "\n Syntax: configdb diff " if @a != 3; Log3('configdb', 4, "configdb: diff requested for device: $param1 in version $param2."); $ret = _cfgDB_Diff($param1, $param2); } when ('info') { Log3('configdb', 4, "info requested."); $ret = _cfgDB_Info; } when ('list') { $param1 = $param1 ? $param1 : '%'; $param2 = $param2 ? $param2 : 0; Log3('configdb', 4, "configdb: list requested for device: $param1 in version $param2."); $ret = _cfgDB_List($param1,$param2); } when ('migrate') { return "\n Migration not possible. Already running with configDB!" if $configfile eq 'configDB'; Log3('configdb', 4, "configdb: migration requested."); $ret = _cfgDB_Migrate; } when ('recover') { return "\n Syntax: configdb recover " if @a != 2; Log3('configdb', 4, "configdb: recover for version $param1 requested."); $ret = _cfgDB_Recover($param1); } when ('reorg') { $param1 = $param1 ? $param1 : 3; Log3('configdb', 4, "configdb: reorg requested with keep: $param1."); $ret = _cfgDB_Reorg($a[1]); } when ('uuid') { $param1 = _cfgDB_Uuid; Log3('configdb', 4, "configdb: uuid requested: $param1"); $ret = $param1; } default { $ret = "\n Syntax:\n". " configdb attr [attribute] [value]\n". " configdb backup\n". " configdb diff \n". " configdb export [version]\n". " configdb import \n". " configdb info\n". " configdb list [device] [version]\n". " configdb migrate\n". " configdb recover \n". " configdb reorg [keepVersions]\n". " configdb uuid\n". ""; } } return $ret; } 1; =pod =begin html

configdb

    Starting with version 5079, fhem can be used with a configuration database instead of a plain text file (e.g. fhem.cfg).
    This offers the possibility to completely waive all cfg-files, "include"-problems and so on.
    Furthermore, configDB offers a versioning of several configuration together with the possibility to restore a former configuration.
    Access to database is provided via perl's database interface DBI.

    Prerequisits / Installation

    • Please install perl package Text::Diff if not already installed on your system.

    • You must have access to a SQL database. Supported database types are SQLITE, MYSQL and POSTGRESQL.

    • The corresponding DBD module must be available in your perl environment,
      e.g. sqlite3 running on a Debian systems requires package libdbd-sqlite3-perl

    • Create an empty database, e.g. with sqlite3:
      	mba:fhem udo$ sqlite3 configDB.db
      
      	SQLite version 3.7.13 2012-07-17 17:46:21
      	Enter ".help" for instructions
      	Enter SQL statements terminated with a ";"
      	sqlite> pragma auto_vacuum=2;
      	sqlite> .quit
      
      	mba:fhem udo$ 
      			
    • The database tables will be created automatically.

    • Create a configuration file containing the connection string to access database.

      IMPORTANT:

      • This file must be named "configDB.conf"
      • This file must be located in your fhem main directory, e.g. /opt/fhem

      ## for MySQL
      ################################################################
      #%dbconfig= (
      #	connection => "mysql:database=configDB;host=db;port=3306",
      #	user => "fhemuser",
      #	password => "fhempassword",
      #);
      ################################################################
      #
      ## for PostgreSQL
      ################################################################
      #%dbconfig= (
      #        connection => "Pg:database=configDB;host=localhost",
      #        user => "fhemuser",
      #        password => "fhempassword"
      #);
      ################################################################
      #
      ## for SQLite (username and password stay empty for SQLite)
      ################################################################
      #%dbconfig= (
      #        connection => "SQLite:dbname=/opt/fhem/configDB.db",
      #        user => "",
      #        password => ""
      #);
      ################################################################
      			

    Start with a complete new "fresh" fhem Installation

      It's easy... simply start fhem by issuing following command:

        perl fhem.pl configDB

      configDB is a keyword which is recognized by fhem to use database for configuration.

      That's all. Everything (save, rereadcfg etc) should work as usual.

    or:

    Migrate your existing fhem configuration into the database

      It's easy, too...

    • start your fhem the last time with fhem.cfg

        perl fhem.pl fhem.cfg


    • transfer your existing configuration into the database

        enter

        configdb migrate

        into frontend's command line


      Be patient! Migration can take some time, especially on mini-systems like RaspberryPi or Beaglebone.
      Completed migration will be indicated by showing database statistics.
      Your original configfile will not be touched or modified by this step.

    • shutdown fhem

    • restart fhem with keyword configDB

        perl fhem.pl configDB

    • configDB is a keyword which is recognized by fhem to use database for configuration.

      That's all. Everything (save, rereadcfg etc) should work as usual.


    Additional functions provided

      A new command configdb is propagated to fhem.
      This command can be used with different parameters.

    • configdb attr [attribute] [value]

    • Provides the possibility to pass attributes to backend and frontend.

      configdb attr private 1 - set the attribute named 'private' to value 1.

      configdb attr private - delete the attribute named 'private'

      configdb attr - show all defined attributes.

      Currently, only one attribute is supported. If 'private' is set to 1 the user and password info
      will not be shown in 'configdb info' output.

    • configdb backup

    • Replaces fhem's default backup process, since backup is no longer supported
      with activated configDB.

      Important:
      Please be aware you are responsible for data backup of your database yourself!
      The backup command can and will not do this job for you!

    • configdb diff <device> <version>

    • Compare configuration dataset for device <device> from current version 0 with version <version>
      Example for valid request:

      get configDB telnetPort 1

      will show a result like this:
      compare device: telnetPort in current version 0 (left) to version: 1 (right)
      +--+--------------------------------------+--+--------------------------------------+
      | 1|define telnetPort telnet 7072 global  | 1|define telnetPort telnet 7072 global  |
      * 2|attr telnetPort room telnet           *  |                                      |
      +--+--------------------------------------+--+--------------------------------------+
    • configdb export <targetFilename> [version];

    • Exports specified version from config database into file <targetFilename>
      Default version if not specified = 0
      The target file can be imported again, if needed.

    • configdb info

    • Returns some database statistics
      --------------------------------------------------------------------------------
       configDB Database Information
      --------------------------------------------------------------------------------
       dbconn: SQLite:dbname=/opt/fhem/configDB.db
       dbuser: 
       dbpass: 
       dbtype: SQLITE
      --------------------------------------------------------------------------------
       fhemconfig: 7707 entries
      
       Ver 0 saved: Sat Mar  1 11:37:00 2014 def: 293 attr: 1248
       Ver 1 saved: Fri Feb 28 23:55:13 2014 def: 293 attr: 1248
       Ver 2 saved: Fri Feb 28 23:49:01 2014 def: 293 attr: 1248
       Ver 3 saved: Fri Feb 28 22:24:40 2014 def: 293 attr: 1247
       Ver 4 saved: Fri Feb 28 22:14:03 2014 def: 293 attr: 1246
      --------------------------------------------------------------------------------
       fhemstate: 1890 entries saved: Sat Mar  1 12:05:00 2014
      --------------------------------------------------------------------------------
      
      Ver 0 always indicates the currently running configuration.

    • configdb list [device] [version]

    • Search for device named [device] in configuration version [version]
      in database archive.
      Default value for [device] = % to show all devices.
      Default value for [version] = 0 to show devices from current version.
      Examples for valid requests:

      get configDB list
      get configDB list global
      get configDB list '' 1
      get configDB list global 1

    • configdb recover <version>

    • Restores an older version from database archive.
      set configDB recover 3 will copy version #3 from database to version #0.
      Original version #0 will be lost.

      Important!
      The restored version will NOT be activated automatically!
      You must do a rereadcfg or - even better - shutdown restart yourself.

    • configdb reorg [keep]

    • Deletes all stored versions with version number higher than [keep].
      Default value for optional parameter keep = 3.
      This function can be used to create a nightly running job for
      database reorganisation when called from an at-Definition.

    • configdb uuid

    • Returns a uuid that can be used for own purposes.



    Author's notes

    • You can find two template files for datebase and configfile (sqlite only!) for easy installation.
      Just copy them to your fhem installation directory (/opt/fhem) and have fun.

    • The frontend option "Edit files"->"config file" will be removed when running configDB.

    • Please be patient when issuing a "save" command (either manually or by clicking on "save config").
      This will take some moments, due to writing version informations.
      Finishing the save-process will be indicated by a corresponding message in frontend.

    • There still will be some more (planned) development to this extension, especially regarding some perfomance issues.

    • Have fun!
=end html =begin html_DE

configdb

    Seit version 5079 unterstützt fhem die Verwendung einer SQL Datenbank zum Abspeichern der kompletten Konfiguration
    Dadurch kann man auf alle cfg Dateien, includes usw. verzichten und die daraus immer wieder resultierenden Probleme vermeiden.
    Desweiteren gibt es damit eine Versionierung von Konfigurationen und die Möglichkeit, jederzeit eine ältere Version wiederherstellen zu können.
    Der Zugriff auf die Datenbank erfolgt über die perl-eigene Datenbankschnittstelle DBI.

    Voraussetzungen / Installation

    • Bitte das perl Paket Text::Diff installieren, falls noch nicht auf dem System vorhanden.

    • Es muss eine SQL Datenbank verfügbar sein, untsrstützt werden SQLITE, MYSQL und POSTGRESQLL.

    • Das zum Datenbanktype gehörende DBD Modul muss in perl installiert sein,
      für sqlite3 auf einem Debian System z.B. das Paket libdbd-sqlite3-perl

    • Eine leere Datenbank muss angelegt werden, z.B. in sqlite3:
      	mba:fhem udo$ sqlite3 configDB.db
      
      	SQLite version 3.7.13 2012-07-17 17:46:21
      	Enter ".help" for instructions
      	Enter SQL statements terminated with a ";"
      	sqlite> pragma auto_vacuum=2;
      	sqlite> .quit
      
      	mba:fhem udo$ 
      			
    • Die benötigten Datenbanktabellen werden automatisch angelegt.

    • Eine Konfigurationsdatei für die Verbindung zur Datenbank muss angelegt werden.

      WICHTIG:

      • Diese Datei muss den Namen "configDB.conf" haben
      • Diese Datei muss im fhem Verzeichnis liegen, z.B. /opt/fhem

      ## für MySQL
      ################################################################
      #%dbconfig= (
      #	connection => "mysql:database=configDB;host=db;port=3306",
      #	user => "fhemuser",
      #	password => "fhempassword",
      #);
      ################################################################
      #
      ## für PostgreSQL
      ################################################################
      #%dbconfig= (
      #        connection => "Pg:database=configDB;host=localhost",
      #        user => "fhemuser",
      #        password => "fhempassword"
      #);
      ################################################################
      #
      ## für SQLite (username and password bleiben bei SQLite leer)
      ################################################################
      #%dbconfig= (
      #        connection => "SQLite:dbname=/opt/fhem/configDB.db",
      #        user => "",
      #        password => ""
      #);
      ################################################################
      			

    Aufruf mit einer vollständig neuen fhem Installation

      Sehr einfach... fhem muss lediglich folgendermassen gestartet werden:

        perl fhem.pl configDB

      configDB ist das Schlüsselwort, an dem fhem erkennt,
      dass eine Datenbank für die Konfiguration verwendet werden soll.

      Das war es schon. Alle Befehle (save, rereadcfg etc) arbeiten wie gewohnt.

    oder:

    übertragen einer bestehenden fhem Konfiguration in die Datenbank

      Auch sehr einfach...

    • fhem wird zum letzten Mal mit der fhem.cfg gestartet

        perl fhem.pl fhem.cfg


    • Bestehende Konfiguration in die Datenbank übertragen

        configdb migrate

        in die Befehlszeile der fhem-Oberfläche eingeben


      Nicht die Geduld verlieren! Die Migration eine Weile dauern, speziell bei Mini-Systemen wie
      RaspberryPi or Beaglebone.
      Am Ende der Migration wird eine aktuelle Datenbankstatistik angezeigt.
      Die ursprüngliche Konfigurationsdatei wird bei diesem Vorgang nicht angetastet.

    • fhem beenden.

    • fhem mit dem Schlüsselwort configDB starten

        perl fhem.pl configDB

    • configDB ist das Schlüsselwort, an dem fhem erkennt,
      dass eine Datenbank für die Konfiguration verwendet werden soll.

      Das war es schon. Alle Befehle (save, rereadcfg etc) arbeiten wie gewohnt.


    Zusätzliche Funktionen

      Es wird ein neuer Befehl configdb bereitgestellt,
      der mit verschiedenen Parametern aufgerufen werden kann.

    • configdb attr [attribute] [value]

    • Hiermit lassen sich attribute setzen, die das Verhalten von Front- und Backend beeinflussen.

      configdb attr private 1 - setzt das Attribut 'private' auf den Wert 1.

      configdb attr private - löscht das Attribut 'private'

      configdb attr - zeigt alle gespeicherten Attribute

      Im Moment ist nur ein Attribut definiert. Wenn 'private' auf 1 gesetzt wird, werden bei 'configdb info'
      keine Benutzer- und Passwortdaten angezeigt.


    • configdb backup

    • Ersetzt den Standard-Backup-Befehl von fhem, da dieser bei Verwendung von configDB nicht mehr
      zur Verfügung steht.

      Wichtig:
      Für die Sicherung der Datenbank ist der Anwender selbst verantwortlich!
      Der backup Befehl kann diese Aufgabe nicht übernehmen.
      Ausnahme: Nutzer einer im fhem Verzeichnis liegenden sqlite Datenbank profitieren von der Einfachheit
      dieser Datenbank, denn das fhem Verzeichnis wird ohnehin komplett gesichert.

    • configdb diff <device> <version>

    • Vergleicht die Konfigurationsdaten des Gerätes <device> aus der aktuellen Version 0 mit den Daten aus Version <version>
      Beispielaufruf:

      configdb diff telnetPort 1

      liefert ein Ergebnis ähnlich dieser Ausgabe:
      compare device: telnetPort in current version 0 (left) to version: 1 (right)
      +--+--------------------------------------+--+--------------------------------------+
      | 1|define telnetPort telnet 7072 global  | 1|define telnetPort telnet 7072 global  |
      * 2|attr telnetPort room telnet           *  |                                      |
      +--+--------------------------------------+--+--------------------------------------+
    • configdb export <zielDateiname> [version];

    • Exportiert die angegebene Version aus der Konfigurationsdatenbank in die Datei <zielDateiname>
      Standardversion, falls nicht angegeben = 0
      Die Zieldatei kann später für die Wiederherstellung verwendet werden.

    • configdb info

    • Liefert eine Datenbankstatistik
      --------------------------------------------------------------------------------
       configDB Database Information
      --------------------------------------------------------------------------------
       dbconn: SQLite:dbname=/opt/fhem/configDB.db
       dbuser: 
       dbpass: 
       dbtype: SQLITE
      --------------------------------------------------------------------------------
       fhemconfig: 7707 entries
      
       Ver 0 saved: Sat Mar  1 11:37:00 2014 def: 293 attr: 1248
       Ver 1 saved: Fri Feb 28 23:55:13 2014 def: 293 attr: 1248
       Ver 2 saved: Fri Feb 28 23:49:01 2014 def: 293 attr: 1248
       Ver 3 saved: Fri Feb 28 22:24:40 2014 def: 293 attr: 1247
       Ver 4 saved: Fri Feb 28 22:14:03 2014 def: 293 attr: 1246
      --------------------------------------------------------------------------------
       fhemstate: 1890 entries saved: Sat Mar  1 12:05:00 2014
      --------------------------------------------------------------------------------
      
      Ver 0 bezeichnet immer die aktuell verwendete Konfiguration.

    • configdb list [device] [version]

    • Sucht das Gerät [device] in der Konfiguration der Version [version]
      in der Datenbank.
      Standardwert für [device] = % um alle Geräte anzuzeigen
      Standardwert für [version] = 0 um Geräte in der aktuellen Version anzuzeigen.
      Beispiele für gültige Aufrufe:

      configdb list
      configdb list global
      configdb list '' 1
      configdb list global 1

    • configdb recover <version>

    • Stellt eine ältere Version aus dem Datenbankarchiv wieder her.
      set configDB recover 3 kopiert die Version #3 aus der Datenbank zur Version #0.
      Die ursprüngliche Version #0 wird dabei gelöscht.

      Wichtig!
      Die zurückgeholte Version wird NICHT automatisch aktiviert!
      Ein rereadcfg oder - besser - shutdown restart muss manuell erfolgen.


  • configdb reorg [keep]

  • Löscht alle gespeicherten Konfigurationen mit Versionsnummern größer als [keep].
    Standardwert für den optionalen Parameter keep = 3.
    Mit dieser Funktion läßt sich eine nächtliche Reorganisation per at umsetzen.

  • configdb uuid

  • Liefert eine uuid, die man für eigene Zwecke verwenden kann.

    Hinweise

    • Im Verzeichnis contrib/configDB befinden sich zwei Vorlagen für Datenbank und Konfiguration,
      die durch einfaches Kopieren in das fhem Verzeichnis sofort verwendet werden können (Nur für sqlite!).

    • Der Menüpunkt "Edit files"->"config file" wird bei Verwendung von configDB nicht mehr angezeigt.

    • Beim Speichern einer Konfiguration nicht ungeduldig werden (egal ob manuell oder durch Klicken auf "save config")
      Durch das Schreiben der Versionsinformationen dauert das ein paar Sekunden.
      Der Abschluss des Speichern wird durch eine entsprechende Meldung angezeigt.

    • Diese Erweiterung wird laufend weiterentwickelt. Speziell an der Verbesserung der Performance wird gearbeitet.

    • Viel Spass!
=end html_DE =cut