backups: Use the backup component in all apps

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
Sunil Mohan Adapa 2020-09-28 16:01:49 -07:00 committed by Veiko Aasa
parent f630fb6059
commit fb1898befc
No known key found for this signature in database
GPG Key ID: 478539CAE680674E
105 changed files with 748 additions and 644 deletions

View File

@ -9,13 +9,14 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.config import get_hostname from plinth.modules.config import get_hostname
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.names.components import DomainType from plinth.modules.names.components import DomainType
from plinth.signals import domain_added, domain_removed, post_hostname_change from plinth.signals import domain_added, domain_removed, post_hostname_change
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
# pylint: disable=C0103 # pylint: disable=C0103
@ -76,6 +77,10 @@ class AvahiApp(app_module.App):
daemon = Daemon('daemon-avahi', managed_services[0]) daemon = Daemon('daemon-avahi', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-avahi',
**manifest.backup)
self.add(backup_restore)
if self.is_enabled(): if self.is_enabled():
domain_added.send_robust(sender='avahi', domain_added.send_robust(sender='avahi',
domain_type='domain-type-local', domain_type='domain-type-local',

View File

@ -3,10 +3,8 @@
Application manifest for avahi. Application manifest for avahi.
""" """
from plinth.modules.backups.api import validate as validate_backup
# Services that intend to make themselves discoverable will drop files into # Services that intend to make themselves discoverable will drop files into
# /etc/avahi/services. Currently, we don't intend to make that customizable. # /etc/avahi/services. Currently, we don't intend to make that customizable.
# There is no necessity for backup and restore. This manifest will ensure that # There is no necessity for backup and restore. This manifest will ensure that
# avahi enable/disable setting is preserved. # avahi enable/disable setting is preserved.
backup = validate_backup({}) backup = {}

View File

@ -75,10 +75,10 @@ def _backup_handler(packet, encryption_passphrase=None):
get_valid_filename(packet.path) + '.json') get_valid_filename(packet.path) + '.json')
manifests = { manifests = {
'apps': [{ 'apps': [{
'name': app.name, 'name': component.app.app_id,
'version': app.app.app.info.version, 'version': component.app.info.version,
'backup': app.manifest 'backup': component.manifest
} for app in packet.apps] } for component in packet.components]
} }
with open(manifest_path, 'w') as manifest_file: with open(manifest_path, 'w') as manifest_file:
json.dump(manifests, manifest_file) json.dump(manifests, manifest_file)
@ -124,9 +124,9 @@ def restore_archive_handler(packet, encryption_passphrase=None):
actions.superuser_run('backups', arguments, input=locations_data.encode()) actions.superuser_run('backups', arguments, input=locations_data.encode())
def restore_from_upload(path, apps=None): def restore_from_upload(path, app_ids=None):
"""Restore files from an uploaded .tar.gz backup file""" """Restore files from an uploaded .tar.gz backup file"""
api.restore_apps(_restore_exported_archive_handler, app_names=apps, api.restore_apps(_restore_exported_archive_handler, app_ids=app_ids,
create_subvolume=False, backup_file=path) create_subvolume=False, backup_file=path)

View File

@ -10,79 +10,38 @@ TODO:
- Implement unit tests. - Implement unit tests.
""" """
import importlib
import logging import logging
from plinth import actions, action_utils, module_loader, setup from plinth import action_utils, actions
from plinth import app as app_module
from plinth import setup
from .components import BackupRestore
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def validate(backup):
"""Validate the backup' information schema."""
assert isinstance(backup, dict)
if 'config' in backup:
assert isinstance(backup['config'], dict)
_validate_directories_and_files(backup['config'])
if 'data' in backup:
assert isinstance(backup['data'], dict)
_validate_directories_and_files(backup['data'])
if 'secrets' in backup:
assert isinstance(backup['secrets'], dict)
_validate_directories_and_files(backup['secrets'])
if 'services' in backup:
assert isinstance(backup['services'], list)
for service in backup['services']:
assert isinstance(service, (str, dict))
if isinstance(service, dict):
_validate_service(service)
return backup
def _validate_directories_and_files(section):
"""Validate directories and files keys in a section."""
if 'directories' in section:
assert isinstance(section['directories'], list)
for directory in section['directories']:
assert isinstance(directory, str)
if 'files' in section:
assert isinstance(section['files'], list)
for file_path in section['files']:
assert isinstance(file_path, str)
def _validate_service(service):
"""Validate a service manifest provided as a dictionary."""
assert isinstance(service['name'], str)
assert isinstance(service['type'], str)
assert service['type'] in ('apache', 'uwsgi', 'system')
if service['type'] == 'apache':
assert service['kind'] in ('config', 'site', 'module')
class BackupError: class BackupError:
"""Represent an backup/restore operation error.""" """Represent an backup/restore operation error."""
def __init__(self, error_type, app, hook=None):
def __init__(self, error_type, component, hook=None):
"""Initialize the error object.""" """Initialize the error object."""
self.error_type = error_type self.error_type = error_type
self.app = app self.component = component
self.hook = hook self.hook = hook
def __eq__(self, other_error): def __eq__(self, other_error):
"""Compare to error objects.""" """Compare to error objects."""
return (self.error_type == other_error.error_type return (self.error_type == other_error.error_type
and self.app == other_error.app and self.component == other_error.component
and self.hook == other_error.hook) and self.hook == other_error.hook)
class Packet: class Packet:
"""Information passed to a handlers for backup/restore operations.""" """Information passed to a handlers for backup/restore operations."""
def __init__(self, operation, scope, root, apps=None, path=None):
def __init__(self, operation, scope, root, components=None, path=None):
"""Initialize the packet. """Initialize the packet.
operation is either 'backup' or 'restore. operation is either 'backup' or 'restore.
@ -101,7 +60,7 @@ class Packet:
self.operation = operation self.operation = operation
self.scope = scope self.scope = scope
self.root = root self.root = root
self.apps = apps self.components = components
self.path = path self.path = path
self.errors = [] self.errors = []
@ -112,11 +71,11 @@ class Packet:
def _process_manifests(self): def _process_manifests(self):
"""Look at manifests and fill up the list of directories/files.""" """Look at manifests and fill up the list of directories/files."""
for app in self.apps: for component in self.components:
for section in ['config', 'data', 'secrets']: for section in ['config', 'data', 'secrets']:
self.directories += app.manifest.get(section, {}).get( section = getattr(component, section)
'directories', []) self.directories += section.get('directories', [])
self.files += app.manifest.get(section, {}).get('files', []) self.files += section.get('files', [])
def backup_full(backup_handler, path=None): def backup_full(backup_handler, path=None):
@ -146,25 +105,25 @@ def restore_full(restore_handler):
_switch_to_subvolume(subvolume) _switch_to_subvolume(subvolume)
def backup_apps(backup_handler, path, app_names=None, def backup_apps(backup_handler, path, app_ids=None,
encryption_passphrase=None): encryption_passphrase=None):
"""Backup data belonging to a set of applications.""" """Backup data belonging to a set of applications."""
if not app_names: if not app_ids:
apps = get_all_apps_for_backup() components = get_all_components_for_backup()
else: else:
apps = get_apps_in_order(app_names) components = get_components_in_order(app_ids)
if _is_snapshot_available(): if _is_snapshot_available():
snapshot = _take_snapshot() snapshot = _take_snapshot()
backup_root = snapshot['mount_path'] backup_root = snapshot['mount_path']
snapshotted = True snapshotted = True
else: else:
_lockdown_apps(apps, lockdown=True) _lockdown_apps(components, lockdown=True)
original_state = _shutdown_services(apps) original_state = _shutdown_services(components)
backup_root = '/' backup_root = '/'
snapshotted = False snapshotted = False
packet = Packet('backup', 'apps', backup_root, apps, path) packet = Packet('backup', 'apps', backup_root, components, path)
_run_operation(backup_handler, packet, _run_operation(backup_handler, packet,
encryption_passphrase=encryption_passphrase) encryption_passphrase=encryption_passphrase)
@ -172,29 +131,29 @@ def backup_apps(backup_handler, path, app_names=None,
_delete_snapshot(snapshot) _delete_snapshot(snapshot)
else: else:
_restore_services(original_state) _restore_services(original_state)
_lockdown_apps(apps, lockdown=False) _lockdown_apps(components, lockdown=False)
def restore_apps(restore_handler, app_names=None, create_subvolume=True, def restore_apps(restore_handler, app_ids=None, create_subvolume=True,
backup_file=None, encryption_passphrase=None): backup_file=None, encryption_passphrase=None):
"""Restore data belonging to a set of applications.""" """Restore data belonging to a set of applications."""
if not app_names: if not app_ids:
apps = get_all_apps_for_backup() components = get_all_components_for_backup()
else: else:
apps = get_apps_in_order(app_names) components = get_components_in_order(app_ids)
_install_apps_before_restore(apps) _install_apps_before_restore(components)
if _is_snapshot_available() and create_subvolume: if _is_snapshot_available() and create_subvolume:
subvolume = _create_subvolume(empty=False) subvolume = _create_subvolume(empty=False)
restore_root = subvolume['mount_path'] restore_root = subvolume['mount_path']
else: else:
_lockdown_apps(apps, lockdown=True) _lockdown_apps(components, lockdown=True)
original_state = _shutdown_services(apps) original_state = _shutdown_services(components)
restore_root = '/' restore_root = '/'
subvolume = False subvolume = False
packet = Packet('restore', 'apps', restore_root, apps, backup_file) packet = Packet('restore', 'apps', restore_root, components, backup_file)
_run_operation(restore_handler, packet, _run_operation(restore_handler, packet,
encryption_passphrase=encryption_passphrase) encryption_passphrase=encryption_passphrase)
@ -202,10 +161,10 @@ def restore_apps(restore_handler, app_names=None, create_subvolume=True,
_switch_to_subvolume(subvolume) _switch_to_subvolume(subvolume)
else: else:
_restore_services(original_state) _restore_services(original_state)
_lockdown_apps(apps, lockdown=False) _lockdown_apps(components, lockdown=False)
def _install_apps_before_restore(apps): def _install_apps_before_restore(components):
"""Install/upgrade apps needed before restoring a backup. """Install/upgrade apps needed before restoring a backup.
Upgrading apps to latest version before backups reduces the chance of newer Upgrading apps to latest version before backups reduces the chance of newer
@ -213,92 +172,57 @@ def _install_apps_before_restore(apps):
""" """
modules_to_setup = [] modules_to_setup = []
for backup_app in apps: for component in components:
if backup_app.app.setup_helper.get_state() in ('needs-setup', module = importlib.import_module(component.app.__class__.__module__)
'needs-update'): if module.setup_helper.get_state() in ('needs-setup', 'needs-update'):
modules_to_setup.append(backup_app.name) modules_to_setup.append(component.app.app_id)
setup.run_setup_on_modules(modules_to_setup) setup.run_setup_on_modules(modules_to_setup)
class BackupApp: def _get_backup_restore_component(app):
"""A application that can be backed up and its manifest.""" """Return the backup/restore component of the app."""
def __init__(self, name, app): for component in app.components.values():
"""Initialize object and load manfiest.""" if isinstance(component, BackupRestore):
self.name = name return component
self.app = app
# Has no backup related meta data raise TypeError
def get_all_components_for_backup():
"""Return a list of all components that can be backed up."""
components = []
for app_ in app_module.App.list():
try: try:
self.manifest = app.backup module = importlib.import_module(app_.__class__.__module__)
except AttributeError: if module.setup_helper.get_state() != 'needs-setup':
raise TypeError components.append(_get_backup_restore_component(app_))
self.has_data = bool(app.backup)
def __eq__(self, other_app):
"""Check if this app is same as another."""
return self.name == other_app.name and \
self.app == other_app.app and \
self.manifest == other_app.manifest and \
self.has_data == other_app.has_data
def is_installed(self):
"""Return whether app is installed.
Return true even if the app needs update.
"""
return self.app.setup_helper.get_state() != 'needs-setup'
def run_hook(self, hook, packet):
"""Run a hook inside an application."""
if not hasattr(self.app, hook):
return
try:
getattr(self.app, hook)(packet)
except Exception as exception:
logger.exception(
'Error running backup/restore hook for app %s: %s', self.name,
exception)
packet.errors.append(BackupError('hook', self.app, hook=hook))
def get_all_apps_for_backup():
"""Return a list of all applications that can be backed up."""
apps = []
for module_name, module in module_loader.loaded_modules.items():
try:
backup_app = BackupApp(module_name, module)
if backup_app.is_installed():
apps.append(backup_app)
except TypeError: # Application not available for backup/restore except TypeError: # Application not available for backup/restore
pass pass
return apps return components
def get_apps_in_order(app_names): def get_components_in_order(app_ids):
"""Return a list of app modules in order of dependency.""" """Return a list of backup components in order of app dependencies."""
apps = [] components = []
for module_name, module in module_loader.loaded_modules.items(): for app_ in app_module.App.list():
if module_name in app_names: if app_.app_id in app_ids:
apps.append(BackupApp(module_name, module)) components.append(_get_backup_restore_component(app_))
return apps return components
def _lockdown_apps(apps, lockdown): def _lockdown_apps(components, lockdown):
"""Mark apps as in/out of lockdown mode and disable all user interaction. """Mark apps as in/out of lockdown mode and disable all user interaction.
This is a flag in the app module. It will enforced by a middleware that This is a flag in the app module. It will enforced by a middleware that
will intercept all interaction and show a lockdown message. will intercept all interaction and show a lockdown message.
""" """
for app in apps: for component in components:
app.app.locked = lockdown component.app.locked = lockdown
# XXX: Lockdown the application UI by implementing a middleware
def _is_snapshot_available(): def _is_snapshot_available():
@ -347,6 +271,7 @@ def _switch_to_subvolume(subvolume):
class ServiceHandler: class ServiceHandler:
"""Abstraction to help with service shutdown/restart.""" """Abstraction to help with service shutdown/restart."""
@staticmethod @staticmethod
def create(backup_app, service): def create(backup_app, service):
service_type = 'system' service_type = 'system'
@ -381,6 +306,7 @@ class ServiceHandler:
class SystemServiceHandler(ServiceHandler): class SystemServiceHandler(ServiceHandler):
"""Handle starting and stopping of system services for backup.""" """Handle starting and stopping of system services for backup."""
def __init__(self, backup_app, service): def __init__(self, backup_app, service):
"""Initialize the object.""" """Initialize the object."""
super().__init__(backup_app, service) super().__init__(backup_app, service)
@ -400,6 +326,7 @@ class SystemServiceHandler(ServiceHandler):
class ApacheServiceHandler(ServiceHandler): class ApacheServiceHandler(ServiceHandler):
"""Handle starting and stopping of Apache services for backup.""" """Handle starting and stopping of Apache services for backup."""
def __init__(self, backup_app, service): def __init__(self, backup_app, service):
"""Initialize the object.""" """Initialize the object."""
super().__init__(backup_app, service) super().__init__(backup_app, service)
@ -424,18 +351,19 @@ class ApacheServiceHandler(ServiceHandler):
['enable', '--name', self.web_name, '--kind', self.kind]) ['enable', '--name', self.web_name, '--kind', self.kind])
def _shutdown_services(apps): def _shutdown_services(components):
"""Shutdown all services specified by manifests. """Shutdown all services specified by backup manifests.
- Services are shutdown in the reverse order of the apps listing. - Services are shutdown in the reverse order of the components listing.
Return the current state of the services so they can be restored Return the current state of the services so they can be restored
accurately. accurately.
""" """
state = [] state = []
for app in apps: for component in components:
for service in app.manifest.get('services', []): for service in component.services:
state.append(ServiceHandler.create(app, service)) state.append(ServiceHandler.create(component, service))
for service in reversed(state): for service in reversed(state):
service.stop() service.stop()
@ -480,8 +408,14 @@ def _run_hooks(hook, packet):
""" """
logger.info('Running %s hooks', hook) logger.info('Running %s hooks', hook)
for app in packet.apps: for component in packet.components:
app.run_hook(hook, packet) try:
getattr(component, hook)(packet)
except Exception as exception:
logger.exception(
'Error running backup/restore hook for app %s: %s',
component.app.app_id, exception)
packet.errors.append(BackupError('hook', component, hook=hook))
def _run_operation(handler, packet, encryption_passphrase=None): def _run_operation(handler, packet, encryption_passphrase=None):

View File

@ -24,16 +24,16 @@ from .repository import get_repositories
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _get_app_choices(apps): def _get_app_choices(components):
"""Return a list of check box multiple choices from list of apps.""" """Return a list of check box multiple choices from list of components."""
choices = [] choices = []
for app in apps: for component in components:
name = app.app.app.info.name name = component.app.info.name
if not app.has_data: if not component.has_data:
name = ugettext('{app} (No data to backup)').format( name = ugettext('{app} (No data to backup)').format(
app=app.app.app.info.name) app=component.app.info.name)
choices.append((app.name, name)) choices.append((component.app_id, name))
return choices return choices
@ -59,9 +59,12 @@ class CreateArchiveForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize the form with selectable apps.""" """Initialize the form with selectable apps."""
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
apps = api.get_all_apps_for_backup() components = api.get_all_components_for_backup()
self.fields['selected_apps'].choices = _get_app_choices(apps) choices = _get_app_choices(components)
self.fields['selected_apps'].initial = [app.name for app in apps] self.fields['selected_apps'].choices = choices
self.fields['selected_apps'].initial = [
choice[0] for choice in choices
]
self.fields['repository'].choices = _get_repository_choices() self.fields['repository'].choices = _get_repository_choices()
@ -72,10 +75,13 @@ class RestoreForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize the form with selectable apps.""" """Initialize the form with selectable apps."""
apps = kwargs.pop('apps') components = kwargs.pop('components')
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['selected_apps'].choices = _get_app_choices(apps) choices = _get_app_choices(components)
self.fields['selected_apps'].initial = [app.name for app in apps] self.fields['selected_apps'].choices = choices
self.fields['selected_apps'].initial = [
choice[0] for choice in choices
]
class UploadForm(forms.Form): class UploadForm(forms.Form):

View File

@ -3,9 +3,7 @@
Application manifest for backups. Application manifest for backups.
""" """
from plinth.modules.backups.api import validate as validate_backup
# Currently, backup application does not have any settings. However, settings # Currently, backup application does not have any settings. However, settings
# such as scheduler settings, backup location, secrets to connect to remove # such as scheduler settings, backup location, secrets to connect to remove
# servers need to be backed up. # servers need to be backed up.
backup = validate_backup({}) backup = {}

View File

@ -158,6 +158,7 @@ class BaseBorgRepository(abc.ABC):
def remove(self): def remove(self):
"""Remove a borg repository""" """Remove a borg repository"""
def list_archives(self): def list_archives(self):
"""Return list of archives in this repository.""" """Return list of archives in this repository."""
output = self.run(['list-repo', '--path', self.borg_path]) output = self.run(['list-repo', '--path', self.borg_path])
@ -165,12 +166,12 @@ class BaseBorgRepository(abc.ABC):
return sorted(archives, key=lambda archive: archive['start'], return sorted(archives, key=lambda archive: archive['start'],
reverse=True) reverse=True)
def create_archive(self, archive_name, app_names): def create_archive(self, archive_name, app_ids):
"""Create a new archive in this repository with given name.""" """Create a new archive in this repository with given name."""
archive_path = self._get_archive_path(archive_name) archive_path = self._get_archive_path(archive_name)
passphrase = self.credentials.get('encryption_passphrase', None) passphrase = self.credentials.get('encryption_passphrase', None)
api.backup_apps(_backup_handler, path=archive_path, api.backup_apps(_backup_handler, path=archive_path, app_ids=app_ids,
app_names=app_names, encryption_passphrase=passphrase) encryption_passphrase=passphrase)
def delete_archive(self, archive_name): def delete_archive(self, archive_name):
"""Delete an archive with given name from this repository.""" """Delete an archive with given name from this repository."""
@ -222,6 +223,7 @@ class BaseBorgRepository(abc.ABC):
def get_download_stream(self, archive_name): def get_download_stream(self, archive_name):
"""Return an stream of .tar.gz binary data for a backup archive.""" """Return an stream of .tar.gz binary data for a backup archive."""
class BufferedReader(io.BufferedReader): class BufferedReader(io.BufferedReader):
"""Improve performance of buffered binary streaming. """Improve performance of buffered binary streaming.
@ -235,6 +237,7 @@ class BaseBorgRepository(abc.ABC):
binary data. binary data.
""" """
def __next__(self): def __next__(self):
"""Override to call read() instead of readline().""" """Override to call read() instead of readline()."""
chunk = self.read(io.DEFAULT_BUFFER_SIZE) chunk = self.read(io.DEFAULT_BUFFER_SIZE)
@ -279,11 +282,11 @@ class BaseBorgRepository(abc.ABC):
output = self.run(['get-archive-apps', '--path', archive_path]) output = self.run(['get-archive-apps', '--path', archive_path])
return output.splitlines() return output.splitlines()
def restore_archive(self, archive_name, apps=None): def restore_archive(self, archive_name, app_ids=None):
"""Restore an archive from this repository to the system.""" """Restore an archive from this repository to the system."""
archive_path = self._get_archive_path(archive_name) archive_path = self._get_archive_path(archive_name)
passphrase = self.credentials.get('encryption_passphrase', None) passphrase = self.credentials.get('encryption_passphrase', None)
api.restore_apps(restore_archive_handler, app_names=apps, api.restore_apps(restore_archive_handler, app_ids=app_ids,
create_subvolume=False, backup_file=archive_path, create_subvolume=False, backup_file=archive_path,
encryption_passphrase=passphrase) encryption_passphrase=passphrase)

View File

@ -8,13 +8,18 @@ from unittest.mock import MagicMock, call, patch
import pytest import pytest
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from plinth.app import App
from .. import api, forms, repository from .. import api, forms, repository
from ..components import BackupRestore
# pylint: disable=protected-access # pylint: disable=protected-access
setup_helper = MagicMock()
def _get_test_manifest(name): def _get_test_manifest(name):
return api.validate({ return {
'config': { 'config': {
'directories': ['/etc/' + name + '/config.d/'], 'directories': ['/etc/' + name + '/config.d/'],
'files': ['/etc/' + name + '/config'], 'files': ['/etc/' + name + '/config'],
@ -32,50 +37,42 @@ def _get_test_manifest(name):
'name': name, 'name': name,
'kind': 'site' 'kind': 'site'
}] }]
}) }
def _get_backup_app(name): def _get_backup_component(name):
"""Return a dummy BackupApp object.""" """Return a BackupRestore component."""
return api.BackupApp(name, MagicMock(backup=_get_test_manifest(name))) return BackupRestore(name, **_get_test_manifest(name))
class TestBackupApp: class AppTest(App):
"""Test the BackupApp class.""" """Sample App for testing."""
@staticmethod app_id = 'test-app'
def test_run_hook():
"""Test running a hook on an application."""
packet = api.Packet('backup', 'apps', '/', [])
hook = 'testhook_pre'
app = MagicMock()
backup_app = api.BackupApp('app_name', app)
backup_app.run_hook(hook, packet)
app.testhook_pre.assert_has_calls([call(packet)])
assert not packet.errors
app.testhook_pre.reset_mock() def _get_test_app(name):
app.testhook_pre.side_effect = Exception() """Return an App."""
backup_app.run_hook(hook, packet) app = AppTest()
assert packet.errors == [api.BackupError('hook', app, hook=hook)] app.app_id = name
app._all_apps[name] = app
del app.testhook_pre app.add(_get_backup_component(name + '-component'))
backup_app.run_hook(hook, packet) return app
@pytest.mark.usefixtures('load_cfg') @pytest.mark.usefixtures('load_cfg')
class TestBackupProcesses: class TestBackupProcesses:
"""Test cases for backup processes""" """Test cases for backup processes"""
@staticmethod @staticmethod
def test_packet_process_manifests(): def test_packet_collected_files_directories():
"""Test that directories/files are collected from manifests.""" """Test that directories/files are collected from manifests."""
apps = [_get_backup_app('a'), _get_backup_app('b')] components = [_get_backup_component('a'), _get_backup_component('b')]
packet = api.Packet('backup', 'apps', '/', apps) packet = api.Packet('backup', 'apps', '/', components)
for app in apps: for component in components:
for section in ['config', 'data', 'secrets']: for section in ['config', 'data', 'secrets']:
for directory in app.manifest[section]['directories']: for directory in getattr(component, section)['directories']:
assert directory in packet.directories assert directory in packet.directories
for file_path in app.manifest[section]['files']: for file_path in getattr(component, section)['files']:
assert file_path in packet.files assert file_path in packet.files
@staticmethod @staticmethod
@ -97,50 +94,56 @@ class TestBackupProcesses:
restore_handler.assert_called_once() restore_handler.assert_called_once()
@staticmethod @staticmethod
@patch('plinth.module_loader.loaded_modules.items') @patch('importlib.import_module')
def test_get_all_apps_for_backup(modules): @patch('plinth.app.App.list')
"""Test listing apps supporting backup and needing backup.""" def test_get_all_components_for_backup(apps_list, import_module):
apps = [ """Test listing components supporting backup and needing backup."""
('a', MagicMock(backup=_get_test_manifest('a'))), modules = [MagicMock(), MagicMock(), MagicMock()]
('b', MagicMock(backup=_get_test_manifest('b'))), import_module.side_effect = modules
('c', MagicMock(backup=None)), apps = [_get_test_app('a'), _get_test_app('b'), _get_test_app('c')]
('d', MagicMock()), modules[1].setup_helper.get_state.side_effect = ['needs-setup']
] apps_list.return_value = apps
del apps[3][1].backup
modules.return_value = apps
returned_apps = api.get_all_apps_for_backup() returned_components = api.get_all_components_for_backup()
expected_apps = [ expected_components = [
api.BackupApp('a', apps[0][1]), apps[0].components['a-component'],
api.BackupApp('b', apps[1][1]), apps[2].components['c-component']
api.BackupApp('c', apps[2][1])
] ]
assert returned_apps == expected_apps assert returned_components == expected_components
@staticmethod @staticmethod
@patch('plinth.module_loader.loaded_modules.items') @patch('plinth.app.App.list')
def test_get_apps_in_order(modules): def test_get_components_in_order(apps_list):
"""Test that apps are listed in correct dependency order.""" """Test that components are listed in correct dependency order."""
apps = [ apps = [
('names', MagicMock(backup=_get_test_manifest('names'))), _get_test_app('names'),
('config', MagicMock(backup=_get_test_manifest('config'))), _get_test_app('other'),
_get_test_app('config')
] ]
modules.return_value = apps apps_list.return_value = apps
app_names = ['config', 'names'] app_ids = ['config', 'names']
apps = api.get_apps_in_order(app_names) components = api.get_components_in_order(app_ids)
assert apps[0].name == 'names' assert len(components) == 2
assert apps[1].name == 'config' assert components[0].app_id == 'names'
assert components[1].app_id == 'config'
@staticmethod @staticmethod
def test__lockdown_apps(): def test__lockdown_apps():
"""Test that locked flag is set for each app.""" """Test that locked flag is set for each app."""
app_a = MagicMock(locked=False) apps = [_get_test_app('test-app-1'), _get_test_app('test-app-2')]
app_b = MagicMock(locked=None) components = [
apps = [MagicMock(app=app_a), MagicMock(app=app_b)] apps[0].components['test-app-1-component'],
api._lockdown_apps(apps, True) apps[1].components['test-app-2-component']
assert app_a.locked is True ]
assert app_b.locked is True
api._lockdown_apps(components, True)
assert apps[0].locked
assert apps[1].locked
api._lockdown_apps(components, False)
assert not apps[0].locked
assert not apps[1].locked
@staticmethod @staticmethod
@patch('plinth.action_utils.webserver_is_enabled') @patch('plinth.action_utils.webserver_is_enabled')
@ -148,19 +151,20 @@ class TestBackupProcesses:
@patch('plinth.actions.superuser_run') @patch('plinth.actions.superuser_run')
def test__shutdown_services(run, service_is_running, webserver_is_enabled): def test__shutdown_services(run, service_is_running, webserver_is_enabled):
"""Test that services are stopped in correct order.""" """Test that services are stopped in correct order."""
apps = [_get_backup_app('a'), _get_backup_app('b')] components = [_get_backup_component('a'), _get_backup_component('b')]
service_is_running.return_value = True service_is_running.return_value = True
webserver_is_enabled.return_value = True webserver_is_enabled.return_value = True
state = api._shutdown_services(apps) state = api._shutdown_services(components)
expected_state = [ expected_state = [
api.ServiceHandler.create(apps[0], api.ServiceHandler.create(components[0],
apps[0].manifest['services'][0]), components[0].services[0]),
api.ServiceHandler.create(apps[0], api.ServiceHandler.create(components[0],
apps[0].manifest['services'][1]), components[0].services[1]),
api.ServiceHandler.create(apps[1], api.ServiceHandler.create(components[1],
apps[1].manifest['services'][0]), components[1].services[0]),
api.ServiceHandler.create(apps[1], apps[1].manifest['services'][1]) api.ServiceHandler.create(components[1],
components[1].services[1]),
] ]
assert state == expected_state assert state == expected_state
@ -207,21 +211,26 @@ class TestBackupProcesses:
@staticmethod @staticmethod
def test__run_operation(): def test__run_operation():
"""Test that operation runs handler and app hooks.""" """Test that operation runs handler and app hooks."""
apps = [_get_backup_app('a'), _get_backup_app('b')] components = [_get_backup_component('a'), _get_backup_component('b')]
packet = api.Packet('backup', 'apps', '/', apps) packet = api.Packet('backup', 'apps', '/', components)
packet.apps[0].run_hook = MagicMock() packet.components[0].backup_pre = MagicMock()
packet.apps[1].run_hook = MagicMock() packet.components[0].backup_post = MagicMock()
packet.components[1].backup_pre = MagicMock()
packet.components[1].backup_post = MagicMock()
handler = MagicMock() handler = MagicMock()
api._run_operation(handler, packet) api._run_operation(handler, packet)
handler.assert_has_calls([call(packet, encryption_passphrase=None)]) handler.assert_has_calls([call(packet, encryption_passphrase=None)])
calls = [call('backup_pre', packet), call('backup_post', packet)] calls = [call(packet)]
packet.apps[0].run_hook.assert_has_calls(calls) packet.components[0].backup_pre.assert_has_calls(calls)
packet.apps[1].run_hook.assert_has_calls(calls) packet.components[0].backup_post.assert_has_calls(calls)
packet.components[1].backup_pre.assert_has_calls(calls)
packet.components[1].backup_post.assert_has_calls(calls)
class TestBackupModule: class TestBackupModule:
"""Tests of the backups django module, like views or forms.""" """Tests of the backups django module, like views or forms."""
@staticmethod @staticmethod
def test_file_upload(): def test_file_upload():
# posting a video should fail # posting a video should fail

View File

@ -147,7 +147,7 @@ class BaseRestoreView(SuccessMessageMixin, FormView):
"""Pass additional keyword args for instantiating the form.""" """Pass additional keyword args for instantiating the form."""
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
included_apps = self._get_included_apps() included_apps = self._get_included_apps()
kwargs['apps'] = api.get_apps_in_order(included_apps) kwargs['components'] = api.get_components_in_order(included_apps)
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View File

@ -11,9 +11,10 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.apache.components import Uwsgi, Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -64,7 +65,7 @@ class BepastyApp(app_module.App):
icon_filename='bepasty', icon_filename='bepasty',
short_description=_('File & Snippet Sharing'), short_description=_('File & Snippet Sharing'),
description=_description, manual_page='bepasty', description=_description, manual_page='bepasty',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-bepasty', info.name, menu_item = menu.Menu('menu-bepasty', info.name,
@ -75,7 +76,7 @@ class BepastyApp(app_module.App):
shortcut = frontpage.Shortcut('shortcut-bepasty', info.name, shortcut = frontpage.Shortcut('shortcut-bepasty', info.name,
info.short_description, info.short_description,
info.icon_filename, '/bepasty', info.icon_filename, '/bepasty',
clients=clients) clients=manifest.clients)
self.add(shortcut) self.add(shortcut)
firewall = Firewall('firewall-bepasty', info.name, firewall = Firewall('firewall-bepasty', info.name,
@ -89,6 +90,10 @@ class BepastyApp(app_module.App):
urls=['https://{host}/bepasty/']) urls=['https://{host}/bepasty/'])
self.add(webserver) self.add(webserver)
backup_restore = BackupRestore('backup-restore-bepasty',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('bepasty'), 'name': _('bepasty'),
@ -13,7 +12,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'files': ['/etc/bepasty-freedombox.conf'] 'files': ['/etc/bepasty-freedombox.conf']
}, },
@ -21,4 +20,4 @@ backup = validate_backup({
'directories': ['/var/lib/bepasty'] 'directories': ['/var/lib/bepasty']
}, },
'services': ['uwsgi'], 'services': ['uwsgi'],
}) }

View File

@ -14,10 +14,11 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -96,6 +97,10 @@ class BindApp(app_module.App):
alias=managed_services[1]) alias=managed_services[1])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-bind',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,11 +3,9 @@
Application manifest for bind. Application manifest for bind.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'config': { 'config': {
'files': ['/etc/bind/named.conf.options'] 'files': ['/etc/bind/named.conf.options']
}, },
'services': ['bind9'] 'services': ['bind9']
}) }

View File

@ -13,11 +13,12 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -60,7 +61,7 @@ class CalibreApp(app_module.App):
name=_('calibre'), icon_filename='calibre', name=_('calibre'), icon_filename='calibre',
short_description=_('E-book Library'), short_description=_('E-book Library'),
description=_description, manual_page='Calibre', description=_description, manual_page='Calibre',
clients=clients, clients=manifest.clients,
donation_url='https://calibre-ebook.com/donate') donation_url='https://calibre-ebook.com/donate')
self.add(info) self.add(info)
@ -94,6 +95,10 @@ class CalibreApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-calibre',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('calibre'), 'name': _('calibre'),
@ -13,9 +12,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'directories': ['/var/lib/private/calibre-server-freedombox/'] 'directories': ['/var/lib/private/calibre-server-freedombox/']
}, },
'services': ['calibre-server-freedombox'] 'services': ['calibre-server-freedombox']
}) }

View File

@ -12,12 +12,12 @@ from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules import names from plinth.modules import names
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.signals import domain_added, domain_removed from plinth.signals import domain_added, domain_removed
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import utils from . import manifest, utils
from .manifest import backup, clients # noqa, pylint: disable=unused-import
version = 1 version = 1
@ -65,7 +65,7 @@ class CockpitApp(app_module.App):
icon='fa-wrench', icon_filename='cockpit', icon='fa-wrench', icon_filename='cockpit',
short_description=_('Server Administration'), short_description=_('Server Administration'),
description=_description, manual_page='Cockpit', description=_description, manual_page='Cockpit',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-cockpit', info.name, menu_item = menu.Menu('menu-cockpit', info.name,
@ -92,6 +92,10 @@ class CockpitApp(app_module.App):
daemon = Daemon('daemon-cockpit', managed_services[0]) daemon = Daemon('daemon-cockpit', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-cockpit',
**manifest.backup)
self.add(backup_restore)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed) domain_removed.connect(on_domain_removed)

View File

@ -5,7 +5,6 @@ Application manifest for cockpit.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.modules.backups.api import validate as validate_backup
from plinth.clients import validate from plinth.clients import validate
clients = validate([{ clients = validate([{
@ -20,4 +19,4 @@ clients = validate([{
# triggered on every Plinth domain change (and cockpit application install) and # triggered on every Plinth domain change (and cockpit application install) and
# will set the value of allowed domains correctly. This is the only key the is # will set the value of allowed domains correctly. This is the only key the is
# customized in cockpit.conf. # customized in cockpit.conf.
backup = validate_backup({}) backup = {}

View File

@ -13,11 +13,12 @@ from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules import names from plinth.modules import names
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.letsencrypt.components import LetsEncrypt
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -86,6 +87,10 @@ class CoturnApp(app_module.App):
reserved_usernames=['turnserver']) reserved_usernames=['turnserver'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-coturn',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -1,10 +1,3 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from plinth.modules.backups.api import validate as validate_backup backup = {'secrets': {'directories': ['/etc/coturn']}, 'services': ['coturn']}
backup = validate_backup({
'secrets': {
'directories': ['/etc/coturn']
},
'services': ['coturn']
})

View File

@ -10,8 +10,9 @@ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -79,6 +80,10 @@ class DateTimeApp(app_module.App):
daemon = Daemon('daemon-datetime', managed_services[0]) daemon = Daemon('daemon-datetime', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-datetime',
**manifest.backup)
self.add(backup_restore)
def diagnose(self): def diagnose(self):
"""Run diagnostics and return the results.""" """Run diagnostics and return the results."""
results = super().diagnose() results = super().diagnose()

View File

@ -3,6 +3,4 @@
Application manifest for datetime. Application manifest for datetime.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {'data': {'files': ['/etc/timezone']}}
backup = validate_backup({'data': {'files': ['/etc/timezone']}})

View File

@ -10,11 +10,12 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users import add_user_to_share_group from plinth.modules.users import add_user_to_share_group
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 6 version = 6
@ -50,7 +51,8 @@ class DelugeApp(app_module.App):
app_id=self.app_id, version=version, name=_('Deluge'), app_id=self.app_id, version=version, name=_('Deluge'),
icon_filename='deluge', icon_filename='deluge',
short_description=_('BitTorrent Web Client'), short_description=_('BitTorrent Web Client'),
description=_description, manual_page='Deluge', clients=clients, description=_description, manual_page='Deluge',
clients=manifest.clients,
donation_url='https://www.patreon.com/deluge_cas') donation_url='https://www.patreon.com/deluge_cas')
self.add(info) self.add(info)
@ -88,6 +90,10 @@ class DelugeApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-deluge',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('Deluge'), 'name': _('Deluge'),
@ -14,9 +13,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'directories': ['/var/lib/deluged/.config'] 'directories': ['/var/lib/deluged/.config']
}, },
'services': ['deluged', 'deluge-web'] 'services': ['deluged', 'deluge-web']
}) }

View File

@ -16,8 +16,9 @@ from django.utils.translation import ugettext_noop
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, daemon, glib, menu from plinth import cfg, daemon, glib, menu
from plinth.modules.apache.components import diagnose_url_on_all from plinth.modules.apache.components import diagnose_url_on_all
from plinth.modules.backups.components import BackupRestore
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -57,6 +58,10 @@ class DiagnosticsApp(app_module.App):
'diagnostics:index', parent_url_name='system') 'diagnostics:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-diagnostics',
**manifest.backup)
self.add(backup_restore)
# Check periodically for low RAM space # Check periodically for low RAM space
interval = 180 if cfg.develop else 3600 interval = 180 if cfg.develop else 3600
glib.schedule(interval, _warn_about_low_ram_space) glib.schedule(interval, _warn_about_low_ram_space)

View File

@ -3,6 +3,4 @@
Application manifest for diagnostics. Application manifest for diagnostics.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -52,8 +52,6 @@ _description = [
' federate with other diaspora* pods.') ' federate with other diaspora* pods.')
] ]
from .manifest import clients # noqa pylint:disable=E402 isort:skip
app = None app = None
@ -65,10 +63,12 @@ class DiasporaApp(app_module.App):
def __init__(self): def __init__(self):
"""Create components for the app.""" """Create components for the app."""
super().__init__() super().__init__()
from . import manifest
info = app_module.Info(app_id=self.app_id, version=version, info = app_module.Info(app_id=self.app_id, version=version,
name=_('diaspora*'), icon_filename='diaspora', name=_('diaspora*'), icon_filename='diaspora',
short_description=_('Federated Social Network'), short_description=_('Federated Social Network'),
description=_description, clients=clients) description=_description,
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-diaspora', info.name, menu_item = menu.Menu('menu-diaspora', info.name,

View File

@ -8,12 +8,13 @@ from django.utils.translation import ugettext_lazy as _
from plinth import actions from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.modules.backups.components import BackupRestore
from plinth.modules.names.components import DomainType from plinth.modules.names.components import DomainType
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.signals import domain_added from plinth.signals import domain_added
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -70,6 +71,10 @@ class DynamicDNSApp(app_module.App):
reserved_usernames=['ez-ipupd']) reserved_usernames=['ez-ipupd'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-dynamicdns',
**manifest.backup)
self.add(backup_restore)
current_status = get_status() current_status = get_status()
if current_status['enabled']: if current_status['enabled']:
domain_added.send_robust(sender='dynamicdns', domain_added.send_robust(sender='dynamicdns',

View File

@ -1,5 +1,3 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from plinth.modules.backups.api import validate as validate_backup backup = {'config': {'directories': ['/etc/ez-ipupdate/']}}
backup = validate_backup({'config': {'directories': ['/etc/ez-ipupdate/']}})

View File

@ -16,6 +16,7 @@ from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules import config from plinth.modules import config
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.letsencrypt.components import LetsEncrypt
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
@ -23,7 +24,7 @@ from plinth.signals import (domain_added, post_hostname_change,
pre_hostname_change) pre_hostname_change)
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 3 version = 3
@ -63,7 +64,8 @@ class EjabberdApp(app_module.App):
name=_('ejabberd'), icon_filename='ejabberd', name=_('ejabberd'), icon_filename='ejabberd',
short_description=_('Chat Server'), short_description=_('Chat Server'),
description=_description, description=_description,
manual_page='ejabberd', clients=clients) manual_page='ejabberd',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-ejabberd', info.name, menu_item = menu.Menu('menu-ejabberd', info.name,
@ -107,6 +109,10 @@ class EjabberdApp(app_module.App):
reserved_usernames=['ejabberd']) reserved_usernames=['ejabberd'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-ejabberd',
**manifest.backup)
self.add(backup_restore)
pre_hostname_change.connect(on_pre_hostname_change) pre_hostname_change.connect(on_pre_hostname_change)
post_hostname_change.connect(on_post_hostname_change) post_hostname_change.connect(on_post_hostname_change)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
from plinth.modules.jsxc import manifest as jsxc_manifest from plinth.modules.jsxc import manifest as jsxc_manifest
_clients = validate([{ _clients = validate([{
@ -106,7 +105,7 @@ _clients.extend(jsxc_manifest.clients)
clients = _clients clients = _clients
backup = validate_backup({ backup = {
'config': { 'config': {
'files': ['/etc/ejabberd/ejabberd.yml'] 'files': ['/etc/ejabberd/ejabberd.yml']
}, },
@ -118,4 +117,4 @@ backup = validate_backup({
'directories': ['/etc/ejabberd/letsencrypt/'] 'directories': ['/etc/ejabberd/letsencrypt/']
}, },
'services': ['ejabberd'] 'services': ['ejabberd']
}) }

View File

@ -12,9 +12,10 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.utils import Version, format_lazy, import_from_gi from plinth.utils import Version, format_lazy, import_from_gi
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
gio = import_from_gi('Gio', '2.0') gio = import_from_gi('Gio', '2.0')
glib = import_from_gi('GLib', '2.0') glib = import_from_gi('GLib', '2.0')
@ -74,6 +75,10 @@ class FirewallApp(app_module.App):
daemon = Daemon('daemon-firewall', managed_services[0]) daemon = Daemon('daemon-firewall', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-firewall',
**manifest.backup)
self.add(backup_restore)
def _run_setup(): def _run_setup():
"""Run firewalld setup.""" """Run firewalld setup."""

View File

@ -3,6 +3,4 @@
Application manifest for firewall. Application manifest for firewall.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -13,12 +13,13 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.errors import ActionError from plinth.errors import ActionError
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from . import manifest
from .forms import is_repo_url from .forms import is_repo_url
from .manifest import ( # noqa, pylint: disable=unused-import from .manifest import GIT_REPO_PATH
GIT_REPO_PATH, backup, clients)
version = 1 version = 1
@ -56,7 +57,7 @@ class GitwebApp(app_module.App):
name=_('Gitweb'), icon_filename='gitweb', name=_('Gitweb'), icon_filename='gitweb',
short_description=_('Simple Git Hosting'), short_description=_('Simple Git Hosting'),
description=_description, manual_page='GitWeb', description=_description, manual_page='GitWeb',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-gitweb', info.name, info.short_description, menu_item = menu.Menu('menu-gitweb', info.name, info.short_description,
@ -88,6 +89,10 @@ class GitwebApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = GitwebBackupRestore('backup-restore-gitweb',
**manifest.backup)
self.add(backup_restore)
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup': if setup_helper.get_state() != 'needs-setup':
self.update_service_access() self.update_service_access()
@ -140,6 +145,14 @@ class GitwebWebserverAuth(Webserver):
super().enable() super().enable()
class GitwebBackupRestore(BackupRestore):
"""Component to handle backup/restore for Gitweb."""
def restore_post(self, packet):
"""Update access after restoration of backups."""
app.update_service_access()
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
helper.install(managed_packages) helper.install(managed_packages)
@ -147,11 +160,6 @@ def setup(helper, old_version=None):
helper.call('post', app.enable) helper.call('post', app.enable)
def restore_post(packet):
"""Update access after restoration of backups."""
app.update_service_access()
def repo_exists(name): def repo_exists(name):
"""Check whether a remote repository exists.""" """Check whether a remote repository exists."""
try: try:

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
CONFIG_FILE = '/etc/gitweb-freedombox.conf' CONFIG_FILE = '/etc/gitweb-freedombox.conf'
GIT_REPO_PATH = '/var/lib/git' GIT_REPO_PATH = '/var/lib/git'
@ -35,11 +34,11 @@ clients = validate([
}, },
]) ])
backup = validate_backup({ backup = {
'config': { 'config': {
'files': [CONFIG_FILE] 'files': [CONFIG_FILE]
}, },
'data': { 'data': {
'directories': [GIT_REPO_PATH] 'directories': [GIT_REPO_PATH]
} }
}) }

View File

@ -10,11 +10,12 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.i2p.resources import FAVORITES from plinth.modules.i2p.resources import FAVORITES
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -58,7 +59,8 @@ class I2PApp(app_module.App):
info = app_module.Info( info = app_module.Info(
app_id=self.app_id, version=version, name=_('I2P'), app_id=self.app_id, version=version, name=_('I2P'),
icon_filename='i2p', short_description=_('Anonymity Network'), icon_filename='i2p', short_description=_('Anonymity Network'),
description=_description, manual_page='I2P', clients=clients, description=_description, manual_page='I2P',
clients=manifest.clients,
donation_url='https://geti2p.net/en/get-involved/donate') donation_url='https://geti2p.net/en/get-involved/donate')
self.add(info) self.add(info)
@ -96,6 +98,9 @@ class I2PApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-i2p', **manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -6,7 +6,6 @@ Application manifest for I2P.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
_package_id = 'net.geti2p.i2p' _package_id = 'net.geti2p.i2p'
_download_url = 'https://geti2p.net/download' _download_url = 'https://geti2p.net/download'
@ -36,9 +35,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'secrets': { 'secrets': {
'directories': ['/var/lib/i2p/i2p-config'] 'directories': ['/var/lib/i2p/i2p-config']
}, },
'services': ['i2p'] 'services': ['i2p']
}) }

View File

@ -10,11 +10,12 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -52,7 +53,7 @@ class IkiwikiApp(app_module.App):
name=_('ikiwiki'), icon_filename='ikiwiki', name=_('ikiwiki'), icon_filename='ikiwiki',
short_description=_('Wiki and Blog'), short_description=_('Wiki and Blog'),
description=_description, manual_page='Ikiwiki', description=_description, manual_page='Ikiwiki',
clients=clients, clients=manifest.clients,
donation_url='https://ikiwiki.info/tipjar/') donation_url='https://ikiwiki.info/tipjar/')
self.add(info) self.add(info)
@ -76,6 +77,10 @@ class IkiwikiApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-ikiwiki',
**manifest.backup)
self.add(backup_restore)
def add_shortcut(self, site, title): def add_shortcut(self, site, title):
"""Add an ikiwiki shortcut to frontpage.""" """Add an ikiwiki shortcut to frontpage."""
shortcut = frontpage.Shortcut('shortcut-ikiwiki-' + site, title, shortcut = frontpage.Shortcut('shortcut-ikiwiki-' + site, title,

View File

@ -2,7 +2,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.modules.backups.api import validate as validate_backup
from plinth.clients import validate from plinth.clients import validate
clients = validate([{ clients = validate([{
@ -13,7 +12,4 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup( backup = {'data': {'directories': ['/var/lib/ikiwiki/', '/var/www/ikiwiki/']}}
{'data': {
'directories': ['/var/lib/ikiwiki/', '/var/www/ikiwiki/']
}})

View File

@ -10,10 +10,11 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 3 version = 3
@ -45,7 +46,8 @@ class InfinotedApp(app_module.App):
name=_('infinoted'), icon_filename='infinoted', name=_('infinoted'), icon_filename='infinoted',
short_description=_('Gobby Server'), short_description=_('Gobby Server'),
description=_description, description=_description,
manual_page='Infinoted', clients=clients) manual_page='Infinoted',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-infinoted', info.name, menu_item = menu.Menu('menu-infinoted', info.name,
@ -69,6 +71,10 @@ class InfinotedApp(app_module.App):
listen_ports=[(6523, 'tcp4'), (6523, 'tcp6')]) listen_ports=[(6523, 'tcp4'), (6523, 'tcp6')])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-infinoted',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import cfg from plinth import cfg
from plinth.modules.backups.api import validate as validate_backup
from plinth.clients import validate from plinth.clients import validate
from plinth.utils import format_lazy from plinth.utils import format_lazy
@ -32,7 +31,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'directories': ['/var/lib/infinoted/'] 'directories': ['/var/lib/infinoted/']
}, },
@ -43,4 +42,4 @@ backup = validate_backup({
], ],
}, },
'services': ['infinoted'] 'services': ['infinoted']
}) }

View File

@ -10,10 +10,11 @@ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.web_server import StaticFiles from plinth.web_server import StaticFiles
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -43,7 +44,7 @@ class JSXCApp(app_module.App):
name=_('JSXC'), icon_filename='jsxc', name=_('JSXC'), icon_filename='jsxc',
short_description=_('Chat Client'), short_description=_('Chat Client'),
description=_description, manual_page='JSXC', description=_description, manual_page='JSXC',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-jsxc', info.name, info.short_description, menu_item = menu.Menu('menu-jsxc', info.name, info.short_description,
@ -72,6 +73,10 @@ class JSXCApp(app_module.App):
directory_map=directory_map) directory_map=directory_map)
self.add(static_files) self.add(static_files)
backup_restore = BackupRestore('backup-restore-jsxc',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -4,7 +4,6 @@ from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('JSXC'), 'name': _('JSXC'),
@ -14,4 +13,4 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({}) backup = {}

View File

@ -15,12 +15,12 @@ from plinth import cfg, menu
from plinth.errors import ActionError from plinth.errors import ActionError
from plinth.modules import names from plinth.modules import names
from plinth.modules.apache.components import diagnose_url from plinth.modules.apache.components import diagnose_url
from plinth.modules.backups.components import BackupRestore
from plinth.modules.names.components import DomainType from plinth.modules.names.components import DomainType
from plinth.signals import domain_added, domain_removed, post_module_loading from plinth.signals import domain_added, domain_removed, post_module_loading
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import components from . import components, manifest
from .manifest import backup # noqa, pylint: disable=unused-import
version = 3 version = 3
@ -74,6 +74,10 @@ class LetsEncryptApp(app_module.App):
'letsencrypt:index', parent_url_name='system') 'letsencrypt:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-letsencrypt',
**manifest.backup)
self.add(backup_restore)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed) domain_removed.connect(on_domain_removed)

View File

@ -3,7 +3,5 @@
Application manfiest for letsencrypt. Application manfiest for letsencrypt.
""" """
from plinth.modules.backups.api import validate as validate_backup
# XXX: Backup and restore the Apache site configuration. # XXX: Backup and restore the Apache site configuration.
backup = validate_backup({'secrets': {'directories': ['/etc/letsencrypt/']}}) backup = {'secrets': {'directories': ['/etc/letsencrypt/']}}

View File

@ -16,10 +16,11 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.letsencrypt.components import LetsEncrypt
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 6 version = 6
@ -63,12 +64,11 @@ class MatrixSynapseApp(app_module.App):
def __init__(self): def __init__(self):
"""Create components for the app.""" """Create components for the app."""
super().__init__() super().__init__()
info = app_module.Info(app_id=self.app_id, version=version, info = app_module.Info(
name=_('Matrix Synapse'), app_id=self.app_id, version=version, name=_('Matrix Synapse'),
icon_filename='matrixsynapse', icon_filename='matrixsynapse', short_description=_('Chat Server'),
short_description=_('Chat Server'), description=_description, manual_page='MatrixSynapse',
description=_description, clients=manifest.clients)
manual_page='MatrixSynapse', clients=clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-matrixsynapse', info.name, menu_item = menu.Menu('menu-matrixsynapse', info.name,
@ -106,6 +106,10 @@ class MatrixSynapseApp(app_module.App):
listen_ports=[(8008, 'tcp4'), (8448, 'tcp4')]) listen_ports=[(8008, 'tcp4'), (8448, 'tcp4')])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-matrixsynapse',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
_android_package_id = 'im.vector.app' _android_package_id = 'im.vector.app'
_element_desktop_download_url = 'https://element.io/get-started' _element_desktop_download_url = 'https://element.io/get-started'
@ -44,7 +43,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'directories': ['/etc/matrix-synapse/conf.d/'], 'directories': ['/etc/matrix-synapse/conf.d/'],
'files': [ 'files': [
@ -68,4 +67,4 @@ backup = validate_backup({
] ]
}, },
'services': ['matrix-synapse'] 'services': ['matrix-synapse']
}) }

View File

@ -13,9 +13,10 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 9 version = 9
@ -58,7 +59,8 @@ class MediaWikiApp(app_module.App):
name=_('MediaWiki'), icon_filename='mediawiki', name=_('MediaWiki'), icon_filename='mediawiki',
short_description=_('Wiki'), short_description=_('Wiki'),
description=_description, description=_description,
manual_page='MediaWiki', clients=clients) manual_page='MediaWiki',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-mediawiki', info.name, menu_item = menu.Menu('menu-mediawiki', info.name,
@ -87,6 +89,10 @@ class MediaWikiApp(app_module.App):
daemon = Daemon('daemon-mediawiki', managed_services[0]) daemon = Daemon('daemon-mediawiki', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-mediawiki',
**manifest.backup)
self.add(backup_restore)
class Shortcut(frontpage.Shortcut): class Shortcut(frontpage.Shortcut):
"""Frontpage shortcut for only logged users when in private mode.""" """Frontpage shortcut for only logged users when in private mode."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('MediaWiki'), 'name': _('MediaWiki'),
@ -13,7 +12,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'files': ['/etc/mediawiki/FreedomBoxSettings.php'] 'files': ['/etc/mediawiki/FreedomBoxSettings.php']
}, },
@ -21,4 +20,4 @@ backup = validate_backup({
'directories': ['/var/lib/mediawiki-db/'] 'directories': ['/var/lib/mediawiki-db/']
}, },
'services': ['mediawiki-jobrunner'] 'services': ['mediawiki-jobrunner']
}) }

View File

@ -10,11 +10,12 @@ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -59,7 +60,8 @@ class MinetestApp(app_module.App):
info = app_module.Info( info = app_module.Info(
app_id=self.app_id, version=version, name=_('Minetest'), app_id=self.app_id, version=version, name=_('Minetest'),
icon_filename='minetest', short_description=_('Block Sandbox'), icon_filename='minetest', short_description=_('Block Sandbox'),
description=_description, manual_page='Minetest', clients=clients, description=_description, manual_page='Minetest',
clients=manifest.clients,
donation_url='https://www.minetest.net/get-involved/#donate') donation_url='https://www.minetest.net/get-involved/#donate')
self.add(info) self.add(info)
@ -89,6 +91,10 @@ class MinetestApp(app_module.App):
reserved_usernames=['Debian-minetest']) reserved_usernames=['Debian-minetest'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-minetest',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -2,7 +2,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.modules.backups.api import validate as validate_backup
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
clients = validate([{ clients = validate([{
@ -37,7 +36,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'files': ['/etc/minetest/minetest.conf'] 'files': ['/etc/minetest/minetest.conf']
}, },
@ -45,4 +44,4 @@ backup = validate_backup({
'directories': ['/var/games/minetest-server/'] 'directories': ['/var/games/minetest-server/']
}, },
'services': ['minetest-server'] 'services': ['minetest-server']
}) }

View File

@ -8,10 +8,11 @@ import plinth.app as app_module
from plinth import actions, frontpage, menu from plinth import actions, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup, clients # noqa from . import manifest
version = 2 version = 2
@ -47,7 +48,8 @@ class MiniDLNAApp(app_module.App):
name=_('MiniDLNA'), icon_filename='minidlna', name=_('MiniDLNA'), icon_filename='minidlna',
short_description=_('Simple Media Server'), short_description=_('Simple Media Server'),
description=_description, description=_description,
manual_page='MiniDLNA', clients=clients) manual_page='MiniDLNA',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu( menu_item = menu.Menu(
@ -70,6 +72,10 @@ class MiniDLNAApp(app_module.App):
allowed_groups=list(groups)) allowed_groups=list(groups))
daemon = Daemon('daemon-minidlna', managed_services[0]) daemon = Daemon('daemon-minidlna', managed_services[0])
backup_restore = BackupRestore('backup-restore-minidlna',
**manifest.backup)
self.add(backup_restore)
self.add(menu_item) self.add(menu_item)
self.add(webserver) self.add(webserver)
self.add(firewall) self.add(firewall)

View File

@ -2,124 +2,108 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.modules.backups.api import validate as validate_backup from plinth.clients import store_url, validate
from plinth.clients import validate, store_url
clients = validate([ clients = validate([
{ {
'name': _('vlc'), 'name':
'platforms': [ _('vlc'),
{ 'platforms': [{
'type': 'package', 'type': 'package',
'os': 'gnu-linux', 'os': 'gnu-linux',
'format': 'deb', 'format': 'deb',
'name': 'vlc', 'name': 'vlc',
}, }, {
{ 'type': 'package',
'type': 'package', 'os': 'gnu-linux',
'os': 'gnu-linux', 'format': 'rpm',
'format': 'rpm', 'name': 'vlc',
'name': 'vlc', }, {
}, 'type': 'download',
{ 'os': 'windows',
'type': 'download', 'url': 'https://www.videolan.org/vlc/download-windows.html',
'os': 'windows', }, {
'url': 'https://www.videolan.org/vlc/download-windows.html', 'type': 'download',
}, 'os': 'macos',
{ 'url': 'https://www.videolan.org/vlc/download-macosx.html',
'type': 'download', }, {
'os': 'macos', 'type': 'store',
'url': 'https://www.videolan.org/vlc/download-macosx.html', 'os': 'android',
}, 'store_name': 'google-play',
{ 'url': store_url('google-play', 'org.videolan.vlc')
'type': 'store', }, {
'os': 'android', 'type': 'store',
'store_name': 'google-play', 'os': 'android',
'url': store_url('google-play', 'org.videolan.vlc') 'store_name': 'f-droid',
}, 'url': store_url('f-droid', 'org.videolan.vlc')
{ }]
'type': 'store',
'os': 'android',
'store_name': 'f-droid',
'url': store_url('f-droid', 'org.videolan.vlc')
},
]
}, },
{ {
'name': _('kodi'), 'name':
'platforms': [ _('kodi'),
{ 'platforms': [{
'type': 'package', 'type': 'package',
'os': 'gnu-linux', 'os': 'gnu-linux',
'format': 'deb', 'format': 'deb',
'name': 'kodi', 'name': 'kodi',
}, }, {
{ 'type': 'package',
'type': 'package', 'os': 'gnu-linux',
'os': 'gnu-linux', 'format': 'rpm',
'format': 'rpm', 'name': 'kodi',
'name': 'kodi', }, {
}, 'type': 'download',
{ 'os': 'windows',
'type': 'download', 'url': 'http://kodi.tv/download/',
'os': 'windows', }, {
'url': 'http://kodi.tv/download/', 'type': 'download',
}, 'os': 'macos',
{ 'url': 'http://kodi.tv/download/',
'type': 'download', }, {
'os': 'macos', 'type': 'store',
'url': 'http://kodi.tv/download/', 'os': 'android',
}, 'store_name': 'google-play',
{ 'url': store_url('google-play', 'org.xbmc.kodi')
'type': 'store', }, {
'os': 'android', 'type': 'store',
'store_name': 'google-play', 'os': 'android',
'url': store_url('google-play', 'org.xbmc.kodi') 'store_name': 'f-droid',
}, 'url': store_url('f-droid', 'org.xbmc.kodi')
{ }]
'type': 'store',
'os': 'android',
'store_name': 'f-droid',
'url': store_url('f-droid', 'org.xbmc.kodi')
},
]
}, },
{ {
'name': _('yaacc'), 'name':
'platforms': [ _('yaacc'),
{ 'platforms': [{
'type': 'store', 'type': 'store',
'os': 'android', 'os': 'android',
'store_name': 'f-droid', 'store_name': 'f-droid',
'url': store_url('f-droid', 'de.yaacc') 'url': store_url('f-droid', 'de.yaacc')
}, }]
]
}, },
{ {
'name': _('totem'), 'name':
'platforms': [ _('totem'),
{ 'platforms': [{
'type': 'package', 'type': 'package',
'os': 'gnu-linux', 'os': 'gnu-linux',
'format': 'deb', 'format': 'deb',
'name': 'totem', 'name': 'totem',
}, }, {
{ 'type': 'package',
'type': 'package', 'os': 'gnu-linux',
'os': 'gnu-linux', 'format': 'rpm',
'format': 'rpm', 'name': 'totem',
'name': 'totem', }]
},
]
}, },
]) ])
# TODO: get all media directories from config file # TODO: get all media directories from config file
# for now hard code default media folder. # for now hard code default media folder.
backup = validate_backup({ backup = {
'data': { 'data': {
'files': ['/etc/minidlna.conf'], 'files': ['/etc/minidlna.conf'],
'directories': ['/var/lib/minidlna'] 'directories': ['/var/lib/minidlna']
}, },
'services': ['minidlna'] 'services': ['minidlna']
}) }

View File

@ -10,12 +10,13 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users import add_user_to_share_group from plinth.modules.users import add_user_to_share_group
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -56,7 +57,8 @@ class MLDonkeyApp(app_module.App):
app_id=self.app_id, version=version, name=_('MLDonkey'), app_id=self.app_id, version=version, name=_('MLDonkey'),
icon_filename='mldonkey', icon_filename='mldonkey',
short_description=_('Peer-to-peer File Sharing'), short_description=_('Peer-to-peer File Sharing'),
description=_description, manual_page='MLDonkey', clients=clients) description=_description, manual_page='MLDonkey',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-mldonkey', info.name, menu_item = menu.Menu('menu-mldonkey', info.name,
@ -88,6 +90,10 @@ class MLDonkeyApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-mldonkey',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -6,7 +6,6 @@ Application manifest for mldonkey.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('MLDonkey'), 'name': _('MLDonkey'),
@ -37,7 +36,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'files': [ 'files': [
'/var/lib/mldonkey/bittorrent.ini', '/var/lib/mldonkey/bt_dht.ini', '/var/lib/mldonkey/bittorrent.ini', '/var/lib/mldonkey/bt_dht.ini',
@ -55,4 +54,4 @@ backup = validate_backup({
] ]
}, },
'services': ['mldonkey-server'] 'services': ['mldonkey-server']
}) }

View File

@ -7,9 +7,10 @@ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.modules.backups.components import BackupRestore
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -60,6 +61,10 @@ class MonkeysphereApp(app_module.App):
reserved_usernames=['monkeysphere']) reserved_usernames=['monkeysphere'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-monkeysphere',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,13 +3,11 @@
Application manfiest for monkeysphere. Application manfiest for monkeysphere.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'config': { 'config': {
'directories': ['/etc/monkeysphere/'] 'directories': ['/etc/monkeysphere/']
}, },
'secrets': { 'secrets': {
'directories': ['/var/lib/monkeysphere/'] 'directories': ['/var/lib/monkeysphere/']
} }
}) }

View File

@ -13,12 +13,13 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules import names from plinth.modules import names
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.letsencrypt.components import LetsEncrypt
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import Version from plinth.utils import Version
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -50,7 +51,8 @@ class MumbleApp(app_module.App):
info = app_module.Info( info = app_module.Info(
app_id=self.app_id, version=version, name=_('Mumble'), app_id=self.app_id, version=version, name=_('Mumble'),
icon_filename='mumble', short_description=_('Voice Chat'), icon_filename='mumble', short_description=_('Voice Chat'),
description=_description, manual_page='Mumble', clients=clients, description=_description, manual_page='Mumble',
clients=manifest.clients,
donation_url='https://wiki.mumble.info/wiki/Donate') donation_url='https://wiki.mumble.info/wiki/Donate')
self.add(info) self.add(info)
@ -88,6 +90,10 @@ class MumbleApp(app_module.App):
reserved_usernames=['mumble-server']) reserved_usernames=['mumble-server'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-mumble',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': 'name':
@ -55,9 +54,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'directories': ['/var/lib/mumble-server'] 'directories': ['/var/lib/mumble-server']
}, },
'services': ['mumble-server'] 'services': ['mumble-server']
}) }

View File

@ -9,11 +9,11 @@ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.modules.backups.components import BackupRestore
from plinth.signals import domain_added, domain_removed from plinth.signals import domain_added, domain_removed
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import components from . import components, manifest
from .manifest import backup # noqa, pylint: disable=unused-import
version = 1 version = 1
@ -52,6 +52,10 @@ class NamesApp(app_module.App):
'names:index', parent_url_name='system') 'names:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-names',
**manifest.backup)
self.add(backup_restore)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed) domain_removed.connect(on_domain_removed)

View File

@ -3,6 +3,4 @@
Application manifest for names. Application manifest for names.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -12,11 +12,12 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 4 version = 4
@ -60,7 +61,7 @@ class OpenVPNApp(app_module.App):
name=_('OpenVPN'), icon_filename='openvpn', name=_('OpenVPN'), icon_filename='openvpn',
short_description=_('Virtual Private Network'), short_description=_('Virtual Private Network'),
description=_description, manual_page='OpenVPN', description=_description, manual_page='OpenVPN',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-openvpn', info.name, menu_item = menu.Menu('menu-openvpn', info.name,
@ -92,6 +93,10 @@ class OpenVPNApp(app_module.App):
groups=self.groups) groups=self.groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-openvpn',
**manifest.backup)
self.add(backup_restore)
def is_enabled(self): def is_enabled(self):
"""Return whether all the leader components are enabled. """Return whether all the leader components are enabled.

View File

@ -6,12 +6,11 @@ Application manifest for OpenVPN.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
_package_id = 'de.blinkt.openvpn' _package_id = 'de.blinkt.openvpn'
_download_url = 'https://openvpn.net/community-downloads' _download_url = 'https://openvpn.net/community-downloads'
backup = validate_backup({'secrets': {'directories': ['/etc/openvpn/']}}) backup = {'secrets': {'directories': ['/etc/openvpn/']}}
clients = validate([{ clients = validate([{
'name': 'name':

View File

@ -9,11 +9,11 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.names.components import DomainType from plinth.modules.names.components import DomainType
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import utils from . import manifest, utils
from .manifest import backup # noqa, pylint: disable=unused-import
version = 2 version = 2
@ -80,6 +80,10 @@ class PagekiteApp(app_module.App):
daemon = Daemon('daemon-pagekite', managed_services[0]) daemon = Daemon('daemon-pagekite', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-pagekite',
**manifest.backup)
self.add(backup_restore)
# Register kite name with Name Services module. # Register kite name with Name Services module.
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup' and self.is_enabled(): if setup_helper.get_state() != 'needs-setup' and self.is_enabled():

View File

@ -3,11 +3,9 @@
Application manifest for pagekite. Application manifest for pagekite.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'config': { 'config': {
'directories': ['/etc/pagekite.d/'] 'directories': ['/etc/pagekite.d/']
}, },
'services': ['pagekite'] 'services': ['pagekite']
}) }

View File

@ -9,7 +9,7 @@ from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from .manifest import clients from . import manifest
version = 1 version = 1
@ -45,7 +45,8 @@ class PerformanceApp(app_module.App):
name=_('Performance'), icon='fa-bar-chart', name=_('Performance'), icon='fa-bar-chart',
short_description=_('System Monitoring'), short_description=_('System Monitoring'),
description=_description, description=_description,
manual_page='Performance', clients=clients) manual_page='Performance',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-performance', info.name, menu_item = menu.Menu('menu-performance', info.name,

View File

@ -6,8 +6,9 @@ FreedomBox app for power controls.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module from plinth import app as app_module
from plinth.modules.backups.components import BackupRestore
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -32,4 +33,8 @@ class PowerApp(app_module.App):
description=_description, manual_page='Power') description=_description, manual_page='Power')
self.add(info) self.add(info)
backup_restore = BackupRestore('backup-restore-power',
**manifest.backup)
self.add(backup_restore)
# not in menu, see issue #834 # not in menu, see issue #834

View File

@ -3,6 +3,4 @@
Application manifest for power. Application manifest for power.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -11,11 +11,12 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import diagnose_url from plinth.modules.apache.components import diagnose_url
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.utils import format_lazy
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -82,6 +83,10 @@ class PrivoxyApp(app_module.App):
reserved_usernames=['privoxy']) reserved_usernames=['privoxy'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-privoxy',
**manifest.backup)
self.add(backup_restore)
def diagnose(self): def diagnose(self):
"""Run diagnostics and return the results.""" """Run diagnostics and return the results."""
results = super().diagnose() results = super().diagnose()

View File

@ -3,6 +3,4 @@
Application manifest for privoxy. Application manifest for privoxy.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -13,12 +13,13 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules import names from plinth.modules import names
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.letsencrypt.components import LetsEncrypt
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -59,7 +60,7 @@ class QuasselApp(app_module.App):
name=_('Quassel'), icon_filename='quassel', name=_('Quassel'), icon_filename='quassel',
short_description=_('IRC Client'), short_description=_('IRC Client'),
description=_description, manual_page='Quassel', description=_description, manual_page='Quassel',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-quassel', info.name, menu_item = menu.Menu('menu-quassel', info.name,
@ -96,6 +97,10 @@ class QuasselApp(app_module.App):
reserved_usernames=['quasselcore']) reserved_usernames=['quasselcore'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-quassel',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': 'name':
@ -45,9 +44,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'secrets': { 'secrets': {
'directories': ['/var/lib/quassel/'] 'directories': ['/var/lib/quassel/']
}, },
'services': ['quasselcore'], 'services': ['quasselcore'],
}) }

View File

@ -12,11 +12,12 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.apache.components import Uwsgi, Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import Version, format_lazy from plinth.utils import Version, format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -54,7 +55,8 @@ class RadicaleApp(app_module.App):
name=_('Radicale'), icon_filename='radicale', name=_('Radicale'), icon_filename='radicale',
short_description=_('Calendar and Addressbook'), short_description=_('Calendar and Addressbook'),
description=_description, description=_description,
manual_page='Radicale', clients=clients) manual_page='Radicale',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-radicale', info.name, menu_item = menu.Menu('menu-radicale', info.name,
@ -84,6 +86,10 @@ class RadicaleApp(app_module.App):
reserved_usernames=['radicale']) reserved_usernames=['radicale'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-radicale',
**manifest.backup)
self.add(backup_restore)
def enable(self): def enable(self):
"""Fix missing directories before enabling radicale.""" """Fix missing directories before enabling radicale."""
actions.superuser_run('radicale', ['fix-paths']) actions.superuser_run('radicale', ['fix-paths'])

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': 'name':
@ -79,9 +78,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'directories': ['/var/lib/radicale/'] 'directories': ['/var/lib/radicale/']
}, },
'services': ['uwsgi'] 'services': ['uwsgi']
}) }

View File

@ -9,9 +9,10 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -53,7 +54,8 @@ class RoundcubeApp(app_module.App):
name=_('Roundcube'), icon_filename='roundcube', name=_('Roundcube'), icon_filename='roundcube',
short_description=_('Email Client'), short_description=_('Email Client'),
description=_description, description=_description,
manual_page='Roundcube', clients=clients) manual_page='Roundcube',
clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-roundcube', info.name, menu_item = menu.Menu('menu-roundcube', info.name,
@ -76,6 +78,10 @@ class RoundcubeApp(app_module.App):
urls=['https://{host}/roundcube']) urls=['https://{host}/roundcube'])
self.add(webserver) self.add(webserver)
backup_restore = BackupRestore('backup-restore-roundcube',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('Roundcube'), 'name': _('Roundcube'),
@ -13,4 +12,4 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({}) backup = {}

View File

@ -15,11 +15,12 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 2 version = 2
@ -60,7 +61,8 @@ class SambaApp(app_module.App):
info = app_module.Info( info = app_module.Info(
app_id=self.app_id, version=version, name=_('Samba'), app_id=self.app_id, version=version, name=_('Samba'),
icon_filename='samba', short_description=_('Network File Storage'), icon_filename='samba', short_description=_('Network File Storage'),
manual_page='Samba', description=_description, clients=clients, manual_page='Samba', description=_description,
clients=manifest.clients,
donation_url='https://www.samba.org/samba/donations.html') donation_url='https://www.samba.org/samba/donations.html')
self.add(info) self.add(info)
@ -96,6 +98,23 @@ class SambaApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = SambaBackupRestore('backup-restore-samba',
**manifest.backup)
self.add(backup_restore)
class SambaBackupRestore(BackupRestore):
"""Component to backup/restore Samba."""
def backup_pre(self, packet):
"""Save registry share configuration."""
actions.superuser_run('samba', ['dump-shares'])
def restore_post(self, packet):
"""Restore configuration."""
actions.superuser_run('samba', ['setup'])
actions.superuser_run('samba', ['restore-shares'])
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
@ -148,14 +167,3 @@ def get_shares():
output = actions.superuser_run('samba', ['get-shares']) output = actions.superuser_run('samba', ['get-shares'])
return json.loads(output) return json.loads(output)
def backup_pre(packet):
"""Save registry share configuration."""
actions.superuser_run('samba', ['dump-shares'])
def restore_post(packet):
"""Restore configuration."""
actions.superuser_run('samba', ['setup'])
actions.superuser_run('samba', ['restore-shares'])

View File

@ -6,7 +6,6 @@ Application manifest for Samba.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
SHARES_CONF_BACKUP_FILE = '/var/lib/plinth/backups-data/samba-shares-dump.conf' SHARES_CONF_BACKUP_FILE = '/var/lib/plinth/backups-data/samba-shares-dump.conf'
@ -77,9 +76,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'files': [SHARES_CONF_BACKUP_FILE] 'files': [SHARES_CONF_BACKUP_FILE]
}, },
'services': ['smbd', 'nmbd'] 'services': ['smbd', 'nmbd']
}) }

View File

@ -11,11 +11,11 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.apache.components import Uwsgi, Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
from .manifest import (PUBLIC_ACCESS_SETTING_FILE, clients)
version = 4 version = 4
@ -47,7 +47,8 @@ class SearxApp(app_module.App):
info = app_module.Info( info = app_module.Info(
app_id=self.app_id, version=version, name=_('Searx'), app_id=self.app_id, version=version, name=_('Searx'),
icon_filename='searx', short_description=_('Web Search'), icon_filename='searx', short_description=_('Web Search'),
description=_description, manual_page='Searx', clients=clients, description=_description, manual_page='Searx',
clients=manifest.clients,
donation_url='https://searx.me/static/donate.html') donation_url='https://searx.me/static/donate.html')
self.add(info) self.add(info)
@ -83,6 +84,10 @@ class SearxApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-searx',
**manifest.backup)
self.add(backup_restore)
def set_shortcut_login_required(self, login_required): def set_shortcut_login_required(self, login_required):
"""Change the login_required property of shortcut.""" """Change the login_required property of shortcut."""
shortcut = self.remove('shortcut-searx') shortcut = self.remove('shortcut-searx')
@ -122,7 +127,7 @@ def get_safe_search_setting():
def is_public_access_enabled(): def is_public_access_enabled():
"""Check whether public access is enabled for Searx.""" """Check whether public access is enabled for Searx."""
return os.path.exists(PUBLIC_ACCESS_SETTING_FILE) return os.path.exists(manifest.PUBLIC_ACCESS_SETTING_FILE)
def enable_public_access(): def enable_public_access():

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('Searx'), 'name': _('Searx'),
@ -15,4 +14,4 @@ clients = validate([{
PUBLIC_ACCESS_SETTING_FILE = '/etc/searx/allow_public_access' PUBLIC_ACCESS_SETTING_FILE = '/etc/searx/allow_public_access'
backup = validate_backup({'config': {'files': [PUBLIC_ACCESS_SETTING_FILE]}}) backup = {'config': {'files': [PUBLIC_ACCESS_SETTING_FILE]}}

View File

@ -13,8 +13,9 @@ from django.utils.translation import ugettext_lazy as _
from plinth import actions from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import menu, module_loader from plinth import menu, module_loader
from plinth.modules.backups.components import BackupRestore
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 6 version = 6
@ -50,6 +51,10 @@ class SecurityApp(app_module.App):
'security:index', parent_url_name='system') 'security:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-security',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install the required packages""" """Install the required packages"""

View File

@ -3,9 +3,4 @@
Application manifest for security. Application manifest for security.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {'config': {'files': ['/etc/security/access.d/50freedombox.conf']}}
backup = validate_backup(
{'config': {
'files': ['/etc/security/access.d/50freedombox.conf']
}})

View File

@ -10,7 +10,7 @@ from plinth import frontpage, menu
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from .manifest import clients from . import manifest
version = 1 version = 1
@ -37,7 +37,7 @@ class ShaarliApp(app_module.App):
name=_('Shaarli'), icon_filename='shaarli', name=_('Shaarli'), icon_filename='shaarli',
short_description=_('Bookmarks'), short_description=_('Bookmarks'),
description=_description, manual_page='Shaarli', description=_description, manual_page='Shaarli',
clients=clients) clients=manifest.clients)
self.add(info) self.add(info)
menu_item = menu.Menu('menu-shaarli', info.name, menu_item = menu.Menu('menu-shaarli', info.name,

View File

@ -10,10 +10,11 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 3 version = 3
@ -76,6 +77,10 @@ class ShadowsocksApp(app_module.App):
listen_ports=[(1080, 'tcp4'), (1080, 'tcp6')]) listen_ports=[(1080, 'tcp4'), (1080, 'tcp6')])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-shadowsocks',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,13 +3,11 @@
Application manifest for shadowsocks. Application manifest for shadowsocks.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'secrets': { 'secrets': {
'files': [ 'files': [
'/var/lib/private/shadowsocks-libev/freedombox/freedombox.json' '/var/lib/private/shadowsocks-libev/freedombox/freedombox.json'
] ]
}, },
'services': ['shadowsocks-libev-local@freedombox'] 'services': ['shadowsocks-libev-local@freedombox']
}) }

View File

@ -10,9 +10,10 @@ from django.utils.translation import ugettext_lazy as _
from plinth import actions from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.modules.backups.components import BackupRestore
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -44,6 +45,10 @@ class SharingApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-sharing',
**manifest.backup)
self.add(backup_restore)
def list_shares(): def list_shares():
"""Return a list of shares.""" """Return a list of shares."""

View File

@ -3,9 +3,7 @@
Application manifest for sharing. Application manifest for sharing.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'config': { 'config': {
'files': ['/etc/apache2/conf-available/sharing-freedombox.conf'] 'files': ['/etc/apache2/conf-available/sharing-freedombox.conf']
}, },
@ -14,4 +12,4 @@ backup = validate_backup({
'kind': 'config', 'kind': 'config',
'name': 'sharing-freedombox' 'name': 'sharing-freedombox'
}] }]
}) }

View File

@ -13,8 +13,9 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.modules import storage from plinth.modules import storage
from plinth.modules.backups.components import BackupRestore
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 4 version = 4
@ -61,6 +62,18 @@ class SnapshotApp(app_module.App):
'snapshot:index', parent_url_name='system') 'snapshot:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = SnapshotBackupRestore('backup-restore-snapshot',
**manifest.backup)
self.add(backup_restore)
class SnapshotBackupRestore(BackupRestore):
"""Component to backup/restore snapshot module."""
def restore_post(self, packet):
"""Run after restore."""
actions.superuser_run('snapshot', ['kill-daemon'])
def is_supported(): def is_supported():
"""Return whether snapshots are support on current setup.""" """Return whether snapshots are support on current setup."""
@ -129,8 +142,3 @@ def get_configuration():
'free_space': 'free_space':
output['FREE_LIMIT'], output['FREE_LIMIT'],
} }
def restore_post(packet):
"""Run after restore."""
actions.superuser_run('snapshot', ['kill-daemon'])

View File

@ -3,10 +3,8 @@
Application manifest for snapshot. Application manifest for snapshot.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'config': { 'config': {
'files': ['/etc/snapper/configs/root', '/etc/default/snapper'] 'files': ['/etc/snapper/configs/root', '/etc/default/snapper']
} }
}) }

View File

@ -13,9 +13,10 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from .manifest import backup # noqa, pylint: disable=unused-import from . import manifest
version = 1 version = 1
@ -60,6 +61,9 @@ class SSHApp(app_module.App):
daemon = Daemon('daemon-ssh', managed_services[0]) daemon = Daemon('daemon-ssh', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-ssh', **manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Configure the module.""" """Configure the module."""

View File

@ -3,9 +3,7 @@
Application manifest for ssh. Application manifest for ssh.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'secrets': { 'secrets': {
'files': [ 'files': [
'/etc/ssh/ssh_host_ecdsa_key', '/etc/ssh/ssh_host_ecdsa_key.pub', '/etc/ssh/ssh_host_ecdsa_key', '/etc/ssh/ssh_host_ecdsa_key.pub',
@ -14,4 +12,4 @@ backup = validate_backup({
'/etc/ssh/ssh_host_rsa_key.pub' '/etc/ssh/ssh_host_rsa_key.pub'
] ]
} }
}) }

View File

@ -15,10 +15,10 @@ from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, glib, menu from plinth import cfg, glib, menu
from plinth.errors import ActionError, PlinthError from plinth.errors import ActionError, PlinthError
from plinth.modules.backups.components import BackupRestore
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import udisks2 from . import manifest, udisks2
from .manifest import backup # noqa, pylint: disable=unused-import
version = 4 version = 4
@ -59,6 +59,10 @@ class StorageApp(app_module.App):
'storage:index', parent_url_name='system') 'storage:index', parent_url_name='system')
self.add(menu_item) self.add(menu_item)
backup_restore = BackupRestore('backup-restore-storage',
**manifest.backup)
self.add(backup_restore)
# Check every hour for low disk space, every 3 minutes in debug mode # Check every hour for low disk space, every 3 minutes in debug mode
interval = 180 if cfg.develop else 3600 interval = 180 if cfg.develop else 3600
glib.schedule(interval, warn_about_low_disk_space) glib.schedule(interval, warn_about_low_disk_space)

View File

@ -3,6 +3,4 @@
Application manifest for storage. Application manifest for storage.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {}
backup = validate_backup({})

View File

@ -10,12 +10,13 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups
from plinth.modules.users import add_user_to_share_group from plinth.modules.users import add_user_to_share_group
from plinth.modules.users.components import UsersAndGroups
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 3 version = 3
@ -59,7 +60,8 @@ class SyncthingApp(app_module.App):
name=_('Syncthing'), icon_filename='syncthing', name=_('Syncthing'), icon_filename='syncthing',
short_description=_('File Synchronization'), short_description=_('File Synchronization'),
description=_description, description=_description,
manual_page='Syncthing', clients=clients, manual_page='Syncthing',
clients=manifest.clients,
donation_url='https://syncthing.net/donations/') donation_url='https://syncthing.net/donations/')
self.add(info) self.add(info)
@ -95,6 +97,10 @@ class SyncthingApp(app_module.App):
[SYSTEM_USER], self.groups) [SYSTEM_USER], self.groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-syncthing',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
_package_id = 'com.nutomic.syncthingandroid' _package_id = 'com.nutomic.syncthingandroid'
_download_url = 'https://syncthing.net/' _download_url = 'https://syncthing.net/'
@ -47,9 +46,9 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'secrets': { 'secrets': {
'directories': ['/var/lib/syncthing/.config'] 'directories': ['/var/lib/syncthing/.config']
}, },
'services': ['syncthing@syncthing'] 'services': ['syncthing@syncthing']
}) }

View File

@ -14,11 +14,12 @@ from plinth import app as app_module
from plinth import cfg, frontpage, menu from plinth import cfg, frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver, diagnose_url from plinth.modules.apache.components import Webserver, diagnose_url
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import manifest
from .errors import TahoeConfigurationError from .errors import TahoeConfigurationError
from .manifest import backup # noqa, pylint: disable=unused-import
version = 1 version = 1
@ -87,6 +88,10 @@ class TahoeApp(app_module.App):
daemon = Daemon('daemon-tahoe', managed_services[0]) daemon = Daemon('daemon-tahoe', managed_services[0])
self.add(daemon) self.add(daemon)
backup_restore = BackupRestore('backup-restore-tahoe',
**manifest.backup)
self.add(backup_restore)
def is_enabled(self): def is_enabled(self):
"""Return whether all the leader components are enabled. """Return whether all the leader components are enabled.

View File

@ -3,11 +3,9 @@
Application manfiest for tahoe-lafs. Application manfiest for tahoe-lafs.
""" """
from plinth.modules.backups.api import validate as validate_backup backup = {
backup = validate_backup({
'secrets': { 'secrets': {
'directories': ['/var/lib/tahoe-lafs/'] 'directories': ['/var/lib/tahoe-lafs/']
}, },
'services': ['tahoe-lafs'] 'services': ['tahoe-lafs']
}) }

View File

@ -13,13 +13,13 @@ from plinth import menu
from plinth.daemon import (Daemon, app_is_running, diagnose_netcat, from plinth.daemon import (Daemon, app_is_running, diagnose_netcat,
diagnose_port_listening) diagnose_port_listening)
from plinth.modules.apache.components import diagnose_url from plinth.modules.apache.components import diagnose_url
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.names.components import DomainType from plinth.modules.names.components import DomainType
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.signals import domain_added, domain_removed from plinth.signals import domain_added, domain_removed
from . import utils from . import manifest, utils
from .manifest import backup, clients # noqa, pylint: disable=unused-import
version = 5 version = 5
@ -55,7 +55,7 @@ class TorApp(app_module.App):
name=_('Tor'), icon_filename='tor', name=_('Tor'), icon_filename='tor',
short_description=_('Anonymity Network'), short_description=_('Anonymity Network'),
description=_description, manual_page='Tor', description=_description, manual_page='Tor',
clients=clients, clients=manifest.clients,
donation_url='https://donate.torproject.org/') donation_url='https://donate.torproject.org/')
self.add(info) self.add(info)
@ -87,6 +87,9 @@ class TorApp(app_module.App):
reserved_usernames=['debian-tor']) reserved_usernames=['debian-tor'])
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-tor', **manifest.backup)
self.add(backup_restore)
# Register hidden service name with Name Services module. # Register hidden service name with Name Services module.
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup' and \ if setup_helper.get_state() != 'needs-setup' and \

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import store_url, validate from plinth.clients import store_url, validate
from plinth.modules.backups.api import validate as validate_backup
_orbot_package_id = 'org.torproject.android' _orbot_package_id = 'org.torproject.android'
_tor_browser_download_url = \ _tor_browser_download_url = \
@ -41,7 +40,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'config': { 'config': {
'directories': ['/etc/tor/'] 'directories': ['/etc/tor/']
}, },
@ -49,4 +48,4 @@ backup = validate_backup({
'directories': ['/var/lib/tor/', '/var/lib/tor-instances/'] 'directories': ['/var/lib/tor/', '/var/lib/tor-instances/']
}, },
'services': ['tor@plinth'] 'services': ['tor@plinth']
}) }

View File

@ -12,11 +12,12 @@ from plinth import app as app_module
from plinth import frontpage, menu from plinth import frontpage, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver from plinth.modules.apache.components import Webserver
from plinth.modules.backups.components import BackupRestore
from plinth.modules.firewall.components import Firewall from plinth.modules.firewall.components import Firewall
from plinth.modules.users import add_user_to_share_group from plinth.modules.users import add_user_to_share_group
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from .manifest import backup, clients # noqa, pylint: disable=unused-import from . import manifest
version = 3 version = 3
@ -52,7 +53,8 @@ class TransmissionApp(app_module.App):
icon_filename='transmission', icon_filename='transmission',
short_description=_('BitTorrent Web Client'), short_description=_('BitTorrent Web Client'),
description=_description, manual_page='Transmission', description=_description, manual_page='Transmission',
clients=clients, donation_url='https://transmissionbt.com/donate/') clients=manifest.clients,
donation_url='https://transmissionbt.com/donate/')
self.add(info) self.add(info)
menu_item = menu.Menu('menu-transmission', info.name, menu_item = menu.Menu('menu-transmission', info.name,
@ -84,6 +86,10 @@ class TransmissionApp(app_module.App):
groups=groups) groups=groups)
self.add(users_and_groups) self.add(users_and_groups)
backup_restore = BackupRestore('backup-restore-transmission',
**manifest.backup)
self.add(backup_restore)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""

View File

@ -3,7 +3,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backup
clients = validate([{ clients = validate([{
'name': _('Transmission'), 'name': _('Transmission'),
@ -13,7 +12,7 @@ clients = validate([{
}] }]
}]) }])
backup = validate_backup({ backup = {
'data': { 'data': {
'directories': ['/var/lib/transmission-daemon/.config'] 'directories': ['/var/lib/transmission-daemon/.config']
}, },
@ -21,4 +20,4 @@ backup = validate_backup({
'files': ['/etc/transmission-daemon/settings.json'] 'files': ['/etc/transmission-daemon/settings.json']
}, },
'services': ['transmission-daemon'] 'services': ['transmission-daemon']
}) }

Some files were not shown because too many files have changed in this diff Show More