diff --git a/plinth/modules/snapshot/__init__.py b/plinth/modules/snapshot/__init__.py index fc7187073..f9de65643 100644 --- a/plinth/modules/snapshot/__init__.py +++ b/plinth/modules/snapshot/__init__.py @@ -18,13 +18,14 @@ FreedomBox app to manage filesystem snapshots. """ -import augeas import json +import augeas from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth.menu import main_menu +from plinth.modules import storage from .manifest import backup @@ -54,6 +55,8 @@ manual_page = 'Snapshots' DEFAULT_FILE = '/etc/default/snapper' +fs_types_supported = ['btrfs'] + def init(): """Initialize the module.""" @@ -61,12 +64,19 @@ def init(): menu.add_urlname(name, 'glyphicon-film', 'snapshot:index') +def is_supported(): + """Return whether snapshots are support on current setup.""" + fs_type = storage.get_filesystem_type() + return fs_type in fs_types_supported + + def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call( - 'post', actions.superuser_run, 'snapshot', - ['setup', '--old-version', str(old_version)]) + if is_supported(): + helper.call('post', actions.superuser_run, 'snapshot', + ['setup', '--old-version', + str(old_version)]) def load_augeas(): diff --git a/plinth/modules/snapshot/templates/snapshot_not_supported.html b/plinth/modules/snapshot/templates/snapshot_not_supported.html new file mode 100644 index 000000000..f555a0d08 --- /dev/null +++ b/plinth/modules/snapshot/templates/snapshot_not_supported.html @@ -0,0 +1,32 @@ +{% extends "service-subsubmenu.html" %} +{% comment %} +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} + +{% block configuration %} +
+ {% blocktrans trimmed with fs_types_supported|join:', ' as types_supported %} + Your have a filesystem of type {{ fs_type }}. Snapshots + are currently only available on {{ types_supported }} + filesystems. + {% endblocktrans %} +
+{% endblock %} diff --git a/plinth/modules/snapshot/views.py b/plinth/modules/snapshot/views.py index 33954ff06..9c56170dd 100644 --- a/plinth/modules/snapshot/views.py +++ b/plinth/modules/snapshot/views.py @@ -30,6 +30,7 @@ from django.utils.translation import ugettext_lazy from plinth import actions from plinth.errors import ActionError from plinth.modules import snapshot as snapshot_module +from plinth.modules import storage from . import get_configuration from .forms import SnapshotForm @@ -43,8 +44,24 @@ subsubmenu = [{ }] +def not_supported_view(request): + """Show that snapshots are not supported on the system.""" + template_data = { + 'title': snapshot_module.name, + 'description': snapshot_module.description, + 'fs_type': storage.get_filesystem_type(), + 'fs_types_supported': snapshot_module.fs_types_supported, + 'manual_page': snapshot_module.manual_page, + } + return TemplateResponse(request, 'snapshot_not_supported.html', + template_data) + + def index(request): """Show snapshot list.""" + if not snapshot_module.is_supported(): + return not_supported_view(request) + status = get_configuration() if request.method == 'POST': form = SnapshotForm(request.POST) @@ -66,6 +83,9 @@ def index(request): def manage(request): """Show snapshot list.""" + if not snapshot_module.is_supported(): + return not_supported_view(request) + if request.method == 'POST': if 'create' in request.POST: actions.superuser_run('snapshot', ['create']) @@ -128,10 +148,10 @@ def update_configuration(request, old_status, new_status): messages.success(request, _('Storage snapshots configuration updated')) except ActionError as exception: - messages.error(request, - _('Action error: {0} [{1}] [{2}]').format( - exception.args[0], exception.args[1], - exception.args[2])) + messages.error( + request, + _('Action error: {0} [{1}] [{2}]').format( + exception.args[0], exception.args[1], exception.args[2])) def delete_selected(request): diff --git a/plinth/modules/storage/__init__.py b/plinth/modules/storage/__init__.py index 08dcfe973..99c88de71 100644 --- a/plinth/modules/storage/__init__.py +++ b/plinth/modules/storage/__init__.py @@ -21,10 +21,11 @@ import json import logging import subprocess +import psutil from django.utils.translation import ugettext_lazy as _ -from plinth import service as service_module from plinth import action_utils, actions, cfg +from plinth import service as service_module from plinth.errors import PlinthError from plinth.menu import main_menu from plinth.utils import format_lazy, import_from_gi, is_user_admin @@ -132,6 +133,15 @@ def _get_disks_from_lsblk(): return disks +def get_filesystem_type(mount_point='/'): + """Returns the type of the filesystem mounted at mountpoint.""" + for partition in psutil.disk_partitions(): + if partition.mountpoint == mount_point: + return partition.fstype + + raise ValueError('No such mount point') + + def get_disk_info(mountpoint, request): """Get information about the free space of a drive""" if not is_user_admin(request, cached=True):