0011336: support backup and restore via cli
authorCornelius Weiß <c.weiss@metaways.de>
Wed, 16 Sep 2015 15:50:40 +0000 (17:50 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Mon, 21 Sep 2015 14:55:15 +0000 (16:55 +0200)
https://forge.tine20.org/view.php?id=11336

Change-Id: Ia169496109c8c460b62425c2989aab8d466e8da5
Reviewed-on: http://gerrit.tine20.com/customers/2205
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
scripts/backup/backup-config.sh [deleted file]
scripts/backup/backup-data.sh [deleted file]
scripts/backup/restore-config.sh [deleted file]
scripts/backup/restore-data.sh [deleted file]
tine20/Setup/Backend/Abstract.php
tine20/Setup/Backend/Interface.php
tine20/Setup/Backend/Mysql.php
tine20/Setup/Controller.php
tine20/Setup/Frontend/Cli.php
tine20/Setup/Server/Cli.php

diff --git a/scripts/backup/backup-config.sh b/scripts/backup/backup-config.sh
deleted file mode 100644 (file)
index 6338487..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-BACKUP_PATH=$(mktemp --directory --tmpdir=/tmp/)
-TODAY=$(date +"%Y-%m-%d")
-
-test -d /var/lib/tine20/backup/$TODAY || mkdir -p /var/lib/tine20/backup/$TODAY
-
-cp -ra /etc/tine20/config.inc.php /var/lib/tine20/backup/$TODAY
\ No newline at end of file
diff --git a/scripts/backup/backup-data.sh b/scripts/backup/backup-data.sh
deleted file mode 100644 (file)
index 8282598..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-
-BACKUP_PATH=$(mktemp --directory --tmpdir=/tmp/)
-TODAY=$(date +"%Y-%m-%d")
-
-if [ ! -x /usr/bin/innobackupex ]; then
-    echo "innobackupex not found exiting"
-    exit 1
-fi
-
-if [ ! -x /usr/bin/xtrabackup ]; then
-    echo "xtrabackup not found exiting"
-    exit 1
-fi
-
-# MyISAM and Innodb tables
-innobackupex --defaults-extra-file /etc/tine20/xtrabackup.cnf --no-timestamp $BACKUP_PATH/mysql
-
-# prepare Innodb tables
-xtrabackup --prepare --target-dir=$BACKUP_PATH/mysql
-xtrabackup --prepare --target-dir=$BACKUP_PATH/mysql
-
-
-(cd $BACKUP_PATH/mysql/ && tar cjf ../full_mysql.tar.bz2 .)
-
-rm -rf $BACKUP_PATH/mysql
-
-(cd /var/lib/tine20/files; tar cjf $BACKUP_PATH/tine20_files.tar.bz2 .)
-
-test -d /var/lib/tine20/backup/$TODAY || mkdir -p /var/lib/tine20/backup/$TODAY
-
-mv $BACKUP_PATH/full_mysql.tar.bz2 /var/lib/tine20/backup/$TODAY
-mv $BACKUP_PATH/tine20_files.tar.bz2 /var/lib/tine20/backup/$TODAY
-
-rm -rf $BACKUP_PATH
-
diff --git a/scripts/backup/restore-config.sh b/scripts/backup/restore-config.sh
deleted file mode 100644 (file)
index d157889..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-SOURCE_PATH=$1
-
-if [ -e $SOURCE_PATH/config.inc.php ]; then
-    cp $SOURCE_PATH/config.inc.php /etc/tine20/config.inc.php
-    chown root:www-data /etc/tine20/config.inc.php
-    chmod 0660 /etc/tine20/config.inc.php
-fi
\ No newline at end of file
diff --git a/scripts/backup/restore-data.sh b/scripts/backup/restore-data.sh
deleted file mode 100644 (file)
index b7b788c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-SOURCE_PATH=$1
-
-(cd /var/lib/mysql/; tar xf $SOURCE_PATH/full_mysql.tar.bz2 .)
-(cd /var/lib/tine20/files; tar xf $SOURCE_PATH/tine20_files.tar.bz2 .)
-
-chown -R mysql:mysql /var/lib/mysql/*
-chown -R www-daza:www-data /var/lib/tine20/files/*
\ No newline at end of file
index d82c4e2..debfb15 100644 (file)
@@ -457,7 +457,29 @@ abstract class Setup_Backend_Abstract implements Setup_Backend_Interface
         $dbTable = $this->getExistingSchema($_table->name);
         return $dbTable->equals($_table);
     }
         $dbTable = $this->getExistingSchema($_table->name);
         return $dbTable->equals($_table);
     }
-    
+
+    /**
+     * Backup Database
+     *
+     * @param $options
+     * @throws Setup_Backend_Exception_NotImplemented
+     */
+    public function backup($options)
+    {
+        throw new Setup_Backend_Exception_NotImplemented('backup not yet implemented');
+    }
+
+    /**
+     * Restore Database
+     *
+     * @param $options
+     * @throws Setup_Backend_Exception_NotImplemented
+     */
+    public function restore($options)
+    {
+        throw new Setup_Backend_Exception_NotImplemented('restore not yet implemented');
+    }
+
     /**
      * create the right mysql-statement-snippet for columns/fields
      *
     /**
      * create the right mysql-statement-snippet for columns/fields
      *
index 0136b14..78b59de 100644 (file)
@@ -180,4 +180,19 @@ interface Setup_Backend_Interface
      * @return String
      */
     public function getIndexDeclarations(Setup_Backend_Schema_Index_Abstract $_index, $_tableName = '');
      * @return String
      */
     public function getIndexDeclarations(Setup_Backend_Schema_Index_Abstract $_index, $_tableName = '');
+
+    /**
+     * Backup Database
+     *
+     * @param $options
+     */
+    public function backup($options);
+
+    /**
+     * Restore Database
+     *
+     * @param $options
+     */
+    public function restore($options);
+
 }
 }
index cede0c5..ca912ed 100644 (file)
@@ -320,4 +320,67 @@ class Setup_Backend_Mysql extends Setup_Backend_Abstract
             $this->_db->query("SET FOREIGN_KEY_CHECKS=" . $_value);
         }
     }
             $this->_db->query("SET FOREIGN_KEY_CHECKS=" . $_value);
         }
     }
+
+    /**
+     * Backup Database
+     *
+     * @param $backupDir
+     */
+    public function backup($backupDir)
+    {
+        // hide password from shell via my.cnf
+        $mycnf = $backupDir . '/my.cnf';
+        $this->_createMyConf($mycnf, $this->_config->database);
+
+        $cmd = "mysqldump --defaults-extra-file=$mycnf "
+              ."--single-transaction "
+              ."--opt "
+              . escapeshellarg($this->_config->database->dbname)
+              ." | bzip2 > $backupDir/tine20_mysql.sql.bz2";
+
+        exec($cmd);
+        unlink($mycnf);
+    }
+
+    /**
+     * Restore Database
+     *
+     * @param $backupDir
+     */
+    public function restore($backupDir)
+    {
+        $mysqlBackupFile = $backupDir . '/tine20_mysql.sql.bz2';
+        if (! file_exists($mysqlBackupFile)) {
+            throw new Exception("$mysqlBackupFile not found");
+        }
+
+        // hide password from shell via my.cnf
+        $mycnf = $backupDir . '/my.cnf';
+        $this->_createMyConf($mycnf, $this->_config->database);
+
+        $cmd = "bzcat $mysqlBackupFile"
+             . " | mysql --defaults-extra-file=$mycnf "
+             . escapeshellarg($this->_config->database->dbname);
+
+        exec($cmd);
+        unlink($mycnf);
+    }
+
+    /**
+     * create my.cnf
+     *
+     * @param $path
+     * @param $config
+     */
+    protected function _createMyConf($path, $config)
+    {
+        $mycnfData = <<<EOT
+[client]
+host = {$config->host}
+port = {$config->port}
+user = {$config->username}
+password = {$config->password}
+EOT;
+        file_put_contents($path, $mycnfData);
+    }
 }
 }
index 9e09ddb..cbf0d2d 100644 (file)
@@ -1811,7 +1811,7 @@ class Setup_Controller
         // deactivate cache again
         Tinebase_Core::setupCache(FALSE);
     }
         // deactivate cache again
         Tinebase_Core::setupCache(FALSE);
     }
-    
+
     /**
      * returns TRUE if filesystem is available
      * 
     /**
      * returns TRUE if filesystem is available
      * 
@@ -1822,28 +1822,132 @@ class Setup_Controller
         if ($this->_isFileSystemAvailable === null) {
             try {
                 $session = Tinebase_Session::getSessionNamespace();
         if ($this->_isFileSystemAvailable === null) {
             try {
                 $session = Tinebase_Session::getSessionNamespace();
-                
+
                 if (isset($session->filesystemAvailable)) {
                     $this->_isFileSystemAvailable = $session->filesystemAvailable;
                 if (isset($session->filesystemAvailable)) {
                     $this->_isFileSystemAvailable = $session->filesystemAvailable;
-                    
+
                     return $this->_isFileSystemAvailable;
                 }
             } catch (Zend_Session_Exception $zse) {
                 $session = null;
             }
                     return $this->_isFileSystemAvailable;
                 }
             } catch (Zend_Session_Exception $zse) {
                 $session = null;
             }
-            
+
             $this->_isFileSystemAvailable = (!empty(Tinebase_Core::getConfig()->filesdir) && is_writeable(Tinebase_Core::getConfig()->filesdir));
             $this->_isFileSystemAvailable = (!empty(Tinebase_Core::getConfig()->filesdir) && is_writeable(Tinebase_Core::getConfig()->filesdir));
-            
+
             if ($session instanceof Zend_Session_Namespace) {
                 if (Tinebase_Session::isWritable()) {
                     $session->filesystemAvailable = $this->_isFileSystemAvailable;
                 }
             }
             if ($session instanceof Zend_Session_Namespace) {
                 if (Tinebase_Session::isWritable()) {
                     $session->filesystemAvailable = $this->_isFileSystemAvailable;
                 }
             }
-            
+
             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
                 . ' Filesystem available: ' . ($this->_isFileSystemAvailable ? 'yes' : 'no'));
         }
             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
                 . ' Filesystem available: ' . ($this->_isFileSystemAvailable ? 'yes' : 'no'));
         }
-        
+
         return $this->_isFileSystemAvailable;
     }
         return $this->_isFileSystemAvailable;
     }
+
+    /**
+     * backup
+     *
+     * @param $options array(
+     *      'backupDir'  => string // where to store the backup
+     *      'noTimestamp => bool   // don't append timestamp to backup dir
+     *      'config'     => bool   // backup config
+     *      'db'         => bool   // backup database
+     *      'files'      => bool   // backup files
+     *    )
+     */
+    public function backup($options)
+    {
+        $config = Setup_Core::getConfig();
+
+        $backupDir = isset($options['backupDir']) ? $options['backupDir'] : $config->backupDir;
+        if (! $backupDir) {
+            throw new Exception('backupDir not configured');
+        }
+
+        if (! isset($options['noTimestamp'])) {
+            $backupDir .= '/' . date_create('now', new DateTimeZone('UTC'))->format('Y-m-d-H-i-s');
+        }
+
+        if (!is_dir($backupDir) && !mkdir($backupDir, 0700, true)) {
+            throw new Exception("$backupDir could  not be created");
+        }
+
+        if ($options['config']) {
+            $configFile = stream_resolve_include_path('config.inc.php');
+            $configDir = dirname($configFile);
+
+            $files = file_exists("$configDir/index.php") ? 'config.inc.php' : '.';
+            `cd $configDir; tar cjf $backupDir/tine20_config.tar.bz2 $files`;
+        }
+
+        if ($options['db']) {
+            if (! $this->_backend) {
+                throw new Exception('db not configured, cannot backup');
+            }
+            $this->_backend->backup($backupDir);
+        }
+
+        $filesDir = isset($config->filesdir) ? $config->filesdir : false;
+        if ($options['files'] && $filesDir) {
+            `cd $filesDir; tar cjf $backupDir/tine20_files.tar.bz2 .`;
+        }
+    }
+
+    /**
+     * restore
+     *
+     * @param $options array(
+     *      'backupDir'  => string // location of backup to restore
+     *      'config'     => bool   // restore config
+     *      'db'         => bool   // restore database
+     *      'files'      => bool   // restore files
+     *    )
+     *
+     * @param $options
+     * @throws Exception
+     */
+    public function restore($options)
+    {
+        if (! isset($options['backupDir'])) {
+            throw new Exception("you need to specify the backupDir");
+        }
+
+        if ($options['config']) {
+            $configBackupFile = $options['backupDir']. '/tine20_config.tar.bz2';
+            if (! file_exists($configBackupFile)) {
+                throw new Exception("$configBackupFile not found");
+            }
+
+            $configDir = isset($options['configDir']) ? $options['configDir'] : false;
+            if (!$configDir) {
+                $configFile = stream_resolve_include_path('config.inc.php');
+                if (!$configFile) {
+                    throw new Exception("can't detect configDir, please use configDir option");
+                }
+                $configDir = dirname($configFile);
+            }
+
+            `cd $configDir; tar xf $configBackupFile`;
+        }
+
+        Setup_Core::setupConfig();
+        $config = Setup_Core::getConfig();
+
+        if ($options['db']) {
+            $this->_backend->restore($options['backupDir']);
+        }
+
+        $filesDir = isset($config->filesdir) ? $config->filesdir : false;
+        if ($options['files']) {
+            $filesBackupFile = $options['backupDir'] . '/tine20_files.tar.bz2';
+            if (! file_exists($filesBackupFile)) {
+                throw new Exception("$filesBackupFile not found");
+            }
+
+            `cd $filesDir; tar xf $filesBackupFile`;
+        }
+    }
 }
 }
index 08caea7..16cbd6e 100644 (file)
@@ -76,6 +76,10 @@ class Setup_Frontend_Cli
             $this->_resetDemodata($_opts);
         } elseif(isset($_opts->updateAllImportExportDefinitions)) {
             $this->_updateAllImportExportDefinitions($_opts);
             $this->_resetDemodata($_opts);
         } elseif(isset($_opts->updateAllImportExportDefinitions)) {
             $this->_updateAllImportExportDefinitions($_opts);
+        } elseif(isset($_opts->backup)) {
+            $this->_backup($_opts);
+        } elseif(isset($_opts->restore)) {
+            $this->_restore($_opts);
         }
         
         if ($exitAfterHandle) {
         }
         
         if ($exitAfterHandle) {
@@ -697,7 +701,19 @@ class Setup_Frontend_Cli
 
         return $adminRole;
     }
 
         return $adminRole;
     }
-    
+
+    protected function _backup(Zend_Console_Getopt $_opts)
+    {
+        $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
+        Setup_Controller::getInstance()->backup($options);
+    }
+
+    protected function _restore(Zend_Console_Getopt $_opts)
+    {
+        $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
+        Setup_Controller::getInstance()->restore($options);
+    }
+
     /**
      * parse options
      * 
     /**
      * parse options
      * 
index 5a1aa7b..d20d261 100644 (file)
@@ -53,7 +53,13 @@ class Setup_Server_Cli implements Tinebase_Server_Interface
                          Examples: 
                           setup.php --egw14import /path/to/config.ini',
                 'reset_demodata'            => 'reinstall applications and install Demodata (Needs Admin user)',
                          Examples: 
                           setup.php --egw14import /path/to/config.ini',
                 'reset_demodata'            => 'reinstall applications and install Demodata (Needs Admin user)',
-                'updateAllImportExportDefinitions' => 'update ImportExport definitions for all applications'
+                'updateAllImportExportDefinitions' => 'update ImportExport definitions for all applications',
+                'backup'                    => 'backup config and data
+                         Examples:
+                           setup.php --backup -- config=1 db=1 files=1 backupDir=/backup/tine20 noTimestamp=1',
+                'restore'                   => 'restore config and data
+                         Examples:
+                           setup.php --restore -- config=1 db=1 files=1 backupDir=/backup/tine20',
             ));
             $opts->parse();
         } catch (Zend_Console_Getopt_Exception $e) {
             ));
             $opts->parse();
         } catch (Zend_Console_Getopt_Exception $e) {
@@ -75,7 +81,9 @@ class Setup_Server_Cli implements Tinebase_Server_Interface
             empty($opts->updateAllImportExportDefinitions) &&
             empty($opts->create_admin) && 
             empty($opts->setconfig) && 
             empty($opts->updateAllImportExportDefinitions) &&
             empty($opts->create_admin) && 
             empty($opts->setconfig) && 
-            empty($opts->getconfig))) 
+            empty($opts->backup) &&
+            empty($opts->restore) &&
+            empty($opts->getconfig)))
         {
             echo $opts->getUsageMessage();
             exit;
         {
             echo $opts->getUsageMessage();
             exit;