mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
nextcloud: Add backup/restore
Signed-off-by: Benedek Nagy <contact@nbenedek.me> [sunil: Simplify method signature and name for setting maint. mode] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
3d8967a20a
commit
8ef680f450
@ -8,6 +8,7 @@ from plinth import frontpage, menu
|
||||
from plinth.config import DropinConfigs
|
||||
from plinth.daemon import Daemon, SharedDaemon
|
||||
from plinth.modules.apache.components import Webserver, diagnose_url
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.modules.firewall.components import (Firewall,
|
||||
FirewallLocalProtection)
|
||||
from plinth.package import Packages
|
||||
@ -104,6 +105,10 @@ class NextcloudApp(app_module.App):
|
||||
daemon = SharedDaemon('shared-daemon-nextcloud-mysql', 'mysql')
|
||||
self.add(daemon)
|
||||
|
||||
backup_restore = NextcloudBackupRestore('backup-restore-nextcloud',
|
||||
**manifest.backup)
|
||||
self.add(backup_restore)
|
||||
|
||||
def setup(self, old_version):
|
||||
"""Install and configure the app."""
|
||||
super().setup(old_version)
|
||||
@ -121,3 +126,17 @@ class NextcloudApp(app_module.App):
|
||||
results = super().diagnose()
|
||||
results.append(diagnose_url('docker.com'))
|
||||
return results
|
||||
|
||||
|
||||
class NextcloudBackupRestore(BackupRestore):
|
||||
"""Component to backup/restore Nextcloud."""
|
||||
|
||||
def backup_pre(self, packet):
|
||||
"""Save database contents."""
|
||||
super().backup_pre(packet)
|
||||
privileged.dump_database()
|
||||
|
||||
def restore_post(self, packet):
|
||||
"""Restore database contents."""
|
||||
super().restore_post(packet)
|
||||
privileged.restore_database()
|
||||
|
||||
@ -46,4 +46,14 @@ clients = [{
|
||||
}]
|
||||
}]
|
||||
|
||||
backup = {}
|
||||
backup = {
|
||||
'data': {
|
||||
'directories': [
|
||||
'/var/lib/containers/storage/volumes/nextcloud-volume-fbx/'
|
||||
],
|
||||
'files': [
|
||||
'/var/lib/plinth/backups-data/nextcloud-database.sql',
|
||||
'/etc/redis/redis.conf'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import os
|
||||
import pathlib
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import time
|
||||
@ -32,6 +33,9 @@ NEXTCLOUD_CRON_SERVICE_FILE = pathlib.Path(
|
||||
NEXTCLOUD_CRON_TIMER_FILE = pathlib.Path(
|
||||
f'{SYSTEMD_LOCATION}nextcloud-cron-fbx.timer')
|
||||
|
||||
DB_BACKUP_FILE = pathlib.Path(
|
||||
'/var/lib/plinth/backups-data/nextcloud-database.sql')
|
||||
|
||||
|
||||
@privileged
|
||||
def setup():
|
||||
@ -146,11 +150,22 @@ def _create_database(db_password):
|
||||
_db_file_path = pathlib.Path('/var/lib/mysql/nextcloud_fbx')
|
||||
if _db_file_path.exists():
|
||||
return
|
||||
query = f'''CREATE USER '{DB_USER}'@'{CONTAINER_IP}'
|
||||
IDENTIFIED BY'{db_password}';
|
||||
CREATE DATABASE {DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
GRANT ALL PRIVILEGES ON {DB_NAME}.* TO '{DB_USER}'@'{CONTAINER_IP}';
|
||||
FLUSH PRIVILEGES;'''
|
||||
|
||||
query = f'''CREATE DATABASE {DB_NAME} CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_general_ci;
|
||||
'''
|
||||
subprocess.run(['mysql', '--user', 'root'], input=query.encode(),
|
||||
check=True)
|
||||
_set_db_privileges(db_password)
|
||||
|
||||
|
||||
def _set_db_privileges(db_password):
|
||||
"""Create user, set password and provide permissions on the database."""
|
||||
query = f'''GRANT ALL PRIVILEGES ON {DB_NAME}.* TO
|
||||
'{DB_USER}'@'{CONTAINER_IP}'
|
||||
IDENTIFIED BY'{db_password}';
|
||||
FLUSH PRIVILEGES;
|
||||
'''
|
||||
subprocess.run(['mysql', '--user', 'root'], input=query.encode(),
|
||||
check=True)
|
||||
|
||||
@ -261,10 +276,11 @@ def _remove_db_socket():
|
||||
|
||||
def _drop_database():
|
||||
"""Drop the mysql database that was created during install."""
|
||||
query = f'''DROP DATABASE {DB_NAME};
|
||||
DROP User '{DB_USER}'@'{CONTAINER_IP}';'''
|
||||
subprocess.run(['mysql', '--user', 'root'], input=query.encode(),
|
||||
check=True)
|
||||
with action_utils.service_ensure_running('mysql'):
|
||||
query = f'''DROP DATABASE {DB_NAME};
|
||||
DROP User '{DB_USER}'@'{CONTAINER_IP}';'''
|
||||
subprocess.run(['mysql', '--user', 'root'], input=query.encode(),
|
||||
check=True)
|
||||
|
||||
|
||||
def _generate_secret_key(length=64, chars=None):
|
||||
@ -272,3 +288,62 @@ def _generate_secret_key(length=64, chars=None):
|
||||
chars = chars or (string.ascii_letters + string.digits)
|
||||
rand = random.SystemRandom()
|
||||
return ''.join(rand.choice(chars) for _ in range(length))
|
||||
|
||||
|
||||
def _set_maintenance_mode(on: bool):
|
||||
"""Turn maintenance mode on or off."""
|
||||
_run_occ('maintenance:mode', '--on' if on else '--off')
|
||||
|
||||
|
||||
@privileged
|
||||
def dump_database():
|
||||
"""Dump database to file."""
|
||||
_set_maintenance_mode(True)
|
||||
DB_BACKUP_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with action_utils.service_ensure_running('mysql'):
|
||||
with DB_BACKUP_FILE.open('w', encoding='utf-8') as file_handle:
|
||||
subprocess.run([
|
||||
'mysqldump', '--add-drop-database', '--add-drop-table',
|
||||
'--add-drop-trigger', '--single-transaction',
|
||||
'--default-character-set=utf8mb4', '--user', 'root',
|
||||
'--databases', DB_NAME
|
||||
], stdout=file_handle, check=True)
|
||||
_set_maintenance_mode(False)
|
||||
|
||||
|
||||
@privileged
|
||||
def restore_database():
|
||||
"""Restore database from file."""
|
||||
with action_utils.service_ensure_running('mysql'):
|
||||
with DB_BACKUP_FILE.open('r', encoding='utf-8') as file_handle:
|
||||
subprocess.run(['mysql', '--user', 'root'], stdin=file_handle,
|
||||
check=True)
|
||||
|
||||
_set_db_privileges(_get_dbpassword())
|
||||
|
||||
action_utils.service_restart('redis-server')
|
||||
_set_maintenance_mode(False)
|
||||
|
||||
# Attempts to update UUIDs of user and group entries. By default,
|
||||
# the command attempts to update UUIDs that have been invalidated by
|
||||
# a migration step.
|
||||
_run_occ('ldap:update-uuid')
|
||||
|
||||
# Update the systems data-fingerprint after a backup is restored
|
||||
_run_occ('maintenance:data-fingerprint')
|
||||
|
||||
|
||||
def _get_dbpassword():
|
||||
"""Return the database password from config.php.
|
||||
|
||||
OCC cannot run unless Nextcloud can already connect to the database.
|
||||
"""
|
||||
config_file = ('/var/lib/containers/storage/volumes/nextcloud-volume-fbx'
|
||||
'/_data/config/config.php')
|
||||
with open(config_file, 'r', encoding='utf-8') as config:
|
||||
config_contents = config.read()
|
||||
|
||||
pattern = r"'{}'\s*=>\s*'([^']*)'".format(re.escape('dbpassword'))
|
||||
match = re.search(pattern, config_contents)
|
||||
|
||||
return match.group(1)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user