diff options
author | Simon Rettberg | 2023-03-02 13:47:49 +0100 |
---|---|---|
committer | Simon Rettberg | 2023-03-02 13:47:49 +0100 |
commit | 9db03926a80a910aa9ef338a5ad1d2328e6e6cb7 (patch) | |
tree | edafb8eb40a8703454052b707e2424a907709906 /modules-available/backup | |
parent | [inc/Taskmanager] Update phpdoc (diff) | |
download | slx-admin-9db03926a80a910aa9ef338a5ad1d2328e6e6cb7.tar.gz slx-admin-9db03926a80a910aa9ef338a5ad1d2328e6e6cb7.tar.xz slx-admin-9db03926a80a910aa9ef338a5ad1d2328e6e6cb7.zip |
[backup] Add UI for monthly automatic backup + password support
Diffstat (limited to 'modules-available/backup')
-rw-r--r-- | modules-available/backup/hooks/cron.inc.php | 38 | ||||
-rw-r--r-- | modules-available/backup/inc/backuprestore.inc.php | 13 | ||||
-rw-r--r-- | modules-available/backup/page.inc.php | 57 | ||||
-rw-r--r-- | modules-available/backup/templates/_page.html | 50 |
4 files changed, 143 insertions, 15 deletions
diff --git a/modules-available/backup/hooks/cron.inc.php b/modules-available/backup/hooks/cron.inc.php new file mode 100644 index 00000000..529a3feb --- /dev/null +++ b/modules-available/backup/hooks/cron.inc.php @@ -0,0 +1,38 @@ +<?php + +(function () { + if (date('dH') !== '0101' || (int)date('i') >= 5) + return; + $mode = Property::get(BackupRestore::PROP_AUTO_BACKUP_MODE, BackupRestore::BACKUP_MODE_OFF); + if ($mode === BackupRestore::BACKUP_MODE_OFF) + return; + // DO IT + $password = trim(Property::get(BackupRestore::PROP_AUTO_BACKUP_PASS, '')); + if (empty($password)) { + $password = null; + } + if ($mode === BackupRestore::BACKUP_MODE_VMSTORE) { + $destination = '/srv/openslx/nfs/auto_backups/'; + } else { + $destination = '/root/auto_backups/'; + } + $destination .= 'sat-' . Property::getServerIp() . '-' . date('Y-m-d'); + $task = Taskmanager::submit('BackupRestore', [ + 'mode' => 'backup', + 'password' => $password, + 'destination' => $destination, + ]); + if (!isset($task['id'])) { + EventLog::failure("Could not create automatic backup, Taskmanager down"); + return; + } + $task = Taskmanager::waitComplete($task, 60000); + if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) { + EventLog::failure("Creating backup failed", print_r($task, true)); + return; + } + if (!is_readable($task['data']['backupFile'])) { + EventLog::failure("Backup file '{$task['data']['backupFile']}' is not readable"); + return; + } +})();
\ No newline at end of file diff --git a/modules-available/backup/inc/backuprestore.inc.php b/modules-available/backup/inc/backuprestore.inc.php new file mode 100644 index 00000000..c34c86a2 --- /dev/null +++ b/modules-available/backup/inc/backuprestore.inc.php @@ -0,0 +1,13 @@ +<?php + +class BackupRestore +{ + + const PROP_LAST_BACKUP = 'backup.last-time'; + const PROP_AUTO_BACKUP_MODE = 'backup.auto-mode'; + const BACKUP_MODE_ROOTHOME = 'ROOTHOME'; + const PROP_AUTO_BACKUP_PASS = 'backup.auto-passwd'; + const BACKUP_MODE_VMSTORE = 'VMSTORE'; + const BACKUP_MODE_OFF = 'OFF'; + +}
\ No newline at end of file diff --git a/modules-available/backup/page.inc.php b/modules-available/backup/page.inc.php index c6aa09ae..04cb82b4 100644 --- a/modules-available/backup/page.inc.php +++ b/modules-available/backup/page.inc.php @@ -3,9 +3,8 @@ class Page_Backup extends Page { - const LAST_BACKUP_PROP = 'backup.last-time'; - - private $action = false; + /** @var ?string */ + private $action = null; private $templateData = array(); protected function doPreprocess() @@ -22,6 +21,9 @@ class Page_Backup extends Page } elseif ($this->action === 'restore') { User::assertPermission("restore"); $this->restore(); + } elseif ($this->action === 'config') { + User::assertPermission("config"); + $this->config(); } User::assertPermission('*'); } @@ -31,27 +33,40 @@ class Page_Backup extends Page if ($this->action === 'restore') { // TODO: We're in post mode, redirect with all the taskids first... Render::addTemplate('restore', $this->templateData); } else { - $lastBackup = (int)Property::get(self::LAST_BACKUP_PROP, 0); + $lastBackup = (int)Property::get(BackupRestore::PROP_LAST_BACKUP, 0); if ($lastBackup === 0) { $lastBackup = false; } else { $lastBackup = date('d.m.Y', $lastBackup); } - $params = ['last_backup' => $lastBackup]; - Permission::addGlobalTags($params['perms'], NULL, ['create', 'restore']); + $params = [ + 'last_backup' => $lastBackup, + 'autoBackupEnabled_checked' => Property::get(BackupRestore::PROP_AUTO_BACKUP_MODE, BackupRestore::BACKUP_MODE_OFF) + != BackupRestore::BACKUP_MODE_OFF ? 'checked' : '', + 'autoBackupPw' => Property::get(BackupRestore::PROP_AUTO_BACKUP_PASS, ''), + ]; + Permission::addGlobalTags($params['perms'], NULL, ['create', 'restore', 'config']); Render::addTemplate('_page', $params); } } private function backup() { - $task = Taskmanager::submit('BackupRestore', array('mode' => 'backup')); + $password = trim(Request::post('passwd', '', 'string')); + if (empty($password)) { + $password = null; + } + $task = Taskmanager::submit('BackupRestore', [ + 'mode' => 'backup', + 'password' => $password, + ]); if (!isset($task['id'])) { Message::addError('backup-failed'); Util::redirect('?do=Backup'); } - $task = Taskmanager::waitComplete($task, 30000); + $task = Taskmanager::waitComplete($task, 60000); if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) { + file_put_contents('/tmp/bwlp-backup-error-' . time() . '.txt', json_encode($task['data'])); Taskmanager::addErrorMessage($task); Util::redirect('?do=Backup'); } @@ -62,8 +77,12 @@ class Page_Backup extends Page Message::addError('main.error-read', $task['data']['backupFile']); Util::redirect('?do=Backup'); } + $userFn = 'satellite-backup_' . Property::getServerIp() . '_' . date('Y.m.d-H.i.s') . '.tgz'; + if ($password !== null) { + $userFn .= '.aes'; + } Header('Content-Type: application/octet-stream', true); - Header('Content-Disposition: attachment; filename=' . 'satellite-backup_' . Property::getServerIp() . '_' . date('Y.m.d-H.i.s') . '.tgz'); + Header('Content-Disposition: attachment; filename=' . $userFn); Header('Content-Length: ' . @filesize($task['data']['backupFile'])); while (!feof($fh)) { $data = fread($fh, 16000); @@ -77,7 +96,7 @@ class Page_Backup extends Page } @fclose($fh); @unlink($task['data']['backupFile']); - Property::set(self::LAST_BACKUP_PROP, time()); + Property::set(BackupRestore::PROP_LAST_BACKUP, time()); die(); } @@ -110,9 +129,14 @@ class Page_Backup extends Page $parent = $task['id']; } EventLog::info('Creating backup on ' . Property::getServerIp()); + $password = trim(Request::post('passwd', '', 'string')); + if (empty($password)) { + $password = null; + } // Finally run restore $task = Taskmanager::submit('BackupRestore', array( 'mode' => 'restore', + 'password' => $password, 'backupFile' => $tempfile, 'parentTask' => $parent, 'failOnParentFail' => false, @@ -143,4 +167,17 @@ class Page_Backup extends Page $this->templateData['rebootid'] = $task['id']; } + private function config() + { + $password = trim(Request::post('passwd', '', 'string')); + if (empty($password)) { + $password = null; + } + $mode = Request::post('auto-backup-enabled', 0, 'int') + ? BackupRestore::BACKUP_MODE_VMSTORE : BackupRestore::BACKUP_MODE_OFF; + Property::set(BackupRestore::PROP_AUTO_BACKUP_MODE, $mode); + Property::set(BackupRestore::PROP_AUTO_BACKUP_PASS, $password); + Util::redirect('?do=backup'); + } + } diff --git a/modules-available/backup/templates/_page.html b/modules-available/backup/templates/_page.html index 3e57c033..52dd7e72 100644 --- a/modules-available/backup/templates/_page.html +++ b/modules-available/backup/templates/_page.html @@ -7,13 +7,20 @@ <div class="panel-heading">{{lang_backup}}</div> <div class="panel-body {{perms.create.disabled}}"> <p>{{lang_backupDescription}}</p> + <div class="form-group"> + <label for="passwd-in">{{lang_backupPasswordLabel}}</label> + <input id="passwd-in" type="{{password_type}}" class="form-control" name="passwd" + placeholder="{{lang_backupPasswordPlaceholder}}"> + </div> + <i>{{lang_backupPasswordHint}}</i> <p class="text-right"> {{lang_lastBackup}}: {{^last_backup}}{{lang_unknown}}{{/last_backup}} {{last_backup}} </p> <button id="b-btn" {{perms.create.disabled}} class="btn btn-primary pull-right" type="submit"> - <span class="glyphicon glyphicon-save"></span> {{lang_download}} + <span class="glyphicon glyphicon-save"></span> + {{lang_download}} </button> </div> </div> @@ -26,22 +33,28 @@ <div class="panel-heading">{{lang_restore}}</div> <div class="panel-body {{perms.restore.disabled}}"> <p>{{lang_restoreDescription}}</p> + <label for="file-out">{{lang_restoreFileLabel}}</label> <div class="input-group upload-ex"> - <input type="text" class="form-control" readonly placeholder="{{lang_selectFile}}"> + <input id="file-out" type="text" class="form-control" readonly placeholder="{{lang_selectFile}}"> <span class="input-group-btn"> <span class="btn btn-default btn-file"> {{lang_browseForFile}}… <input type="file" name="backupfile" {{perms.restore.disabled}}> </span> </span> </div> - <div> + <div class="form-group"> + <label for="passwd-out">{{lang_restorePasswordLabel}}</label> + <input id="passwd-out" type="{{password_type}}" class="form-control" name="passwd" + placeholder="{{lang_restorePasswordPlaceholder}}"> + </div> + <div class="form-group"> <div class="checkbox"> <input type="checkbox" name="restore_openslx" checked="checked" id="id-sysonfig"> <label for="id-sysonfig"><b>{{lang_restoreSystemConfig}}</b></label> </div> <p><i>{{lang_systemExplanation}}</i></p> </div> - <div> + <div class="form-group"> <div class="checkbox"> <input type="checkbox" name="restore_dozmod" checked="checked" id="id-dozmod"> <label for="id-dozmod"><b>{{lang_restoreDozmodConfig}}</b></label> @@ -53,7 +66,34 @@ {{lang_runningUploads}}: <span class="uploads">??</span>, {{lang_runningDownloads}}: <span class="downloads">??</span> </div> - <button {{perms.restore.disabled}} class="btn btn-primary pull-right" type="submit"><span class="glyphicon glyphicon-open"></span> {{lang_restore}}</button> + <button {{perms.restore.disabled}} class="btn btn-primary pull-right" type="submit"> + <span class="glyphicon glyphicon-open"></span> + {{lang_restore}} + </button> + </div> + </div> +</form> + +<form action="?do=Backup" method="post"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="config"> + <div class="panel panel-default"> + <div class="panel-heading">{{lang_autoBackupHeading}}</div> + <div class="panel-body {{perms.config.disabled}}"> + <p>{{lang_autoBackupText}}</p> + <div class="checkbox"> + <input id="auto-backup-enabled" type="checkbox" name="auto-backup-enabled" value="1" {{autoBackupEnabled_checked}}> + <label for="auto-backup-enabled">{{lang_autoBackupEnabled}}</label> + </div> + <div class="form-group"> + <label for="passwd-auto">{{lang_autoBackupPasswordLabel}}</label> + <input id="passwd-auto" type="{{password_type}}" class="form-control" name="passwd" + value="{{autoBackupPw}}" placeholder="{{lang_autoBackupPasswordPlaceholder}}"> + </div> + <button {{perms.config.disabled}} class="btn btn-primary pull-right" type="submit"> + <span class="glyphicon glyphicon-floppy-disk"></span> + {{lang_save}} + </button> </div> </div> </form> |