summaryrefslogtreecommitdiffstats
path: root/modules-available/backup
diff options
context:
space:
mode:
authorSimon Rettberg2023-03-02 13:47:49 +0100
committerSimon Rettberg2023-03-02 13:47:49 +0100
commit9db03926a80a910aa9ef338a5ad1d2328e6e6cb7 (patch)
treeedafb8eb40a8703454052b707e2424a907709906 /modules-available/backup
parent[inc/Taskmanager] Update phpdoc (diff)
downloadslx-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.php38
-rw-r--r--modules-available/backup/inc/backuprestore.inc.php13
-rw-r--r--modules-available/backup/page.inc.php57
-rw-r--r--modules-available/backup/templates/_page.html50
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}}&hellip; <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>