diff --git a/plinth/modules/storage/__init__.py b/plinth/modules/storage/__init__.py index cb4fd3357..ffdd9f07b 100644 --- a/plinth/modules/storage/__init__.py +++ b/plinth/modules/storage/__init__.py @@ -27,7 +27,7 @@ from plinth import service as service_module from plinth import action_utils, actions, cfg from plinth.errors import PlinthError from plinth.menu import main_menu -from plinth.utils import format_lazy, is_user_admin +from plinth.utils import format_lazy, import_from_gi, is_user_admin from .manifest import backup @@ -75,27 +75,12 @@ def get_disks(): disk_from_df.update(disk_from_lsblk) combined_list.append(disk_from_df) + for disk in combined_list: + disk['is_root_device'] = is_root_device(disk) + return combined_list -def get_disk_info(mountpoint, request): - """Get information about the free space of a drive""" - if not is_user_admin(request, cached=True): - raise PermissionError - disks = get_disks() - list_root = [disk for disk in disks if disk['mountpoint'] == mountpoint] - if not list_root: - raise PlinthError - percent_used = list_root[0]['percent_used'] - free_bytes = list_root[0]['free'] - free_gib = free_bytes / (1024**3) - return { - "percent_used": percent_used, - "free_bytes": free_bytes, - "free_gib": free_gib - } - - def _get_disks_from_df(): """Return the list of disks and free space available using 'df'.""" command = [ @@ -130,7 +115,9 @@ def _get_disks_from_df(): def _get_disks_from_lsblk(): """Return the list of disks and other information from 'lsblk'.""" - command = ['lsblk', '--json', '--output', 'kname,pkname,mountpoint,type'] + command = [ + 'lsblk', '--json', '--output', 'label,kname,pkname,mountpoint,type' + ] try: process = subprocess.run(command, stdout=subprocess.PIPE, check=True) except subprocess.CalledProcessError as exception: @@ -145,16 +132,35 @@ def _get_disks_from_lsblk(): return disks +def get_disk_info(mountpoint, request): + """Get information about the free space of a drive""" + if not is_user_admin(request, cached=True): + raise PermissionError + disks = get_disks() + list_root = [disk for disk in disks if disk['mountpoint'] == mountpoint] + if not list_root: + raise PlinthError + percent_used = list_root[0]['percent_used'] + free_bytes = list_root[0]['free'] + free_gib = free_bytes / (1024**3) + return { + "percent_used": percent_used, + "free_bytes": free_bytes, + "free_gib": free_gib + } + + def get_root_device(disks): """Return the root partition's device from list of partitions.""" - devices = [ - disk['dev_kname'] for disk in disks - if disk['mountpoint'] == '/' and disk['type'] == 'part' - ] - try: - return devices[0] - except IndexError: - return None + for disk in disks: + if is_root_device(disk): + return disk['dev_kname'] + return None + + +def is_root_device(disk): + """Return whether a given disk is a root device or not.""" + return disk['mountpoint'] == '/' and disk['type'] == 'part' def is_expandable(device): @@ -200,6 +206,49 @@ def format_bytes(size): return _('{disk_size:.1f} TiB').format(disk_size=size) +def get_error_message(error): + """Return an error message given an exception.""" + udisks = import_from_gi('UDisks', '2.0') + if error.matches(udisks.Error.quark(), udisks.Error.FAILED): + message = _('The operation failed.') + elif error.matches(udisks.Error.quark(), udisks.Error.CANCELLED): + message = _('The operation was cancelled.') + elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_UNMOUNTING): + message = _('The device is already unmounting.') + elif error.matches(udisks.Error.quark(), udisks.Error.NOT_SUPPORTED): + message = _('The operation is not supported due to ' + 'missing driver/tool support.') + elif error.matches(udisks.Error.quark(), udisks.Error.TIMED_OUT): + message = _('The operation timed out.') + elif error.matches(udisks.Error.quark(), udisks.Error.WOULD_WAKEUP): + message = _('The operation would wake up a disk that is ' + 'in a deep-sleep state.') + elif error.matches(udisks.Error.quark(), udisks.Error.DEVICE_BUSY): + message = _('Attempting to unmount a device that is busy.') + elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_CANCELLED): + message = _('The operation has already been cancelled.') + elif error.matches(udisks.Error.quark(), udisks.Error.NOT_AUTHORIZED) or \ + error.matches(udisks.Error.quark(), + udisks.Error.NOT_AUTHORIZED_CAN_OBTAIN) or \ + error.matches(udisks.Error.quark(), + udisks.Error.NOT_AUTHORIZED_DISMISSED): + message = _('Not authorized to perform the requested operation.') + elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_MOUNTED): + message = _('The device is already mounted.') + elif error.matches(udisks.Error.quark(), udisks.Error.NOT_MOUNTED): + message = _('The device is not mounted.') + elif error.matches(udisks.Error.quark(), + udisks.Error.OPTION_NOT_PERMITTED): + message = _('Not permitted to use the requested option.') + elif error.matches(udisks.Error.quark(), + udisks.Error.MOUNTED_BY_OTHER_USER): + message = _('The device is mounted by another user.') + else: + message = error.message + + return message + + def is_running(): """Return whether the service is running.""" return action_utils.service_is_running('freedombox-udiskie') diff --git a/plinth/modules/storage/templates/storage.html b/plinth/modules/storage/templates/storage.html index f4f3d9790..3f37fd81c 100644 --- a/plinth/modules/storage/templates/storage.html +++ b/plinth/modules/storage/templates/storage.html @@ -56,15 +56,18 @@ {% trans "Device" %} + {% trans "Label" %} {% trans "Mount Point" %} {% trans "Type" %} {% trans "Used" %} + {% trans "Actions" %} {% for disk in disks %} {{ disk.dev_kname }} + {{ disk.label|default_if_none:"" }} {{ disk.mount_point }} {{ disk.file_system_type }} @@ -84,6 +87,18 @@
{{ disk.used_str }} / {{ disk.size_str }}
+ + {% if not disk.is_root_device %} +
+ {% csrf_token %} + + +
+ {% endif %} + {% endfor %} @@ -110,51 +125,5 @@ {{ block.super }} -

{% trans "Removable Devices" %}

- - {% if not devices %} -

- {% blocktrans trimmed %} - There are no additional storage devices attached. - {% endblocktrans %} -

- {% else %} - - - - - - - - - - - - - {% for device in devices %} - - - - - - - - - {% endfor %} - -
{% trans "Device" %}{% trans "Label" %}{% trans "Mount Point" %}{% trans "Type" %}{% trans "Size" %}{% trans "Actions" %}
{{ device.device }}{{ device.label }}{{ device.mount_points|join:', ' }}{{ device.filesystem_type }}{{ device.size }} - {% if device.mount_points %} -
- {% csrf_token %} - - -
- {% endif %} -
- {% endif %} - {% endblock %} {% endblock %} diff --git a/plinth/modules/storage/udisks2.py b/plinth/modules/storage/udisks2.py deleted file mode 100644 index 85022f5b5..000000000 --- a/plinth/modules/storage/udisks2.py +++ /dev/null @@ -1,105 +0,0 @@ -# -# 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 . -# -""" -Library for interacting with udisks2 D-Bus service. -""" - -from django.utils.translation import ugettext as _ - -from plinth import utils -from plinth.modules.storage import format_bytes - - -def list_devices(): - """List devices that can be ejected.""" - udisks = utils.import_from_gi('UDisks', '2.0') - client = udisks.Client.new_sync() - object_manager = client.get_object_manager() - - block = None - devices = [] - for obj in object_manager.get_objects(): - if not obj.get_block(): - continue - - block = obj.get_block() - if block.props.id_usage != 'filesystem' or \ - block.props.hint_system: - continue - - device_name = block.props.device - if not device_name: - continue - - device = { - 'device': block.props.device, - 'label': block.props.id_label, - 'size': format_bytes(block.props.size), - 'filesystem_type': block.props.id_type - } - - try: - device['mount_points'] = obj.get_filesystem().props.mount_points - except Exception: - pass - - devices.append(device) - - return devices - - -def get_error_message(error): - """Return an error message given an exception.""" - udisks = utils.import_from_gi('UDisks', '2.0') - if error.matches(udisks.Error.quark(), udisks.Error.FAILED): - message = _('The operation failed.') - elif error.matches(udisks.Error.quark(), udisks.Error.CANCELLED): - message = _('The operation was cancelled.') - elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_UNMOUNTING): - message = _('The device is already unmounting.') - elif error.matches(udisks.Error.quark(), udisks.Error.NOT_SUPPORTED): - message = _('The operation is not supported due to ' - 'missing driver/tool support.') - elif error.matches(udisks.Error.quark(), udisks.Error.TIMED_OUT): - message = _('The operation timed out.') - elif error.matches(udisks.Error.quark(), udisks.Error.WOULD_WAKEUP): - message = _('The operation would wake up a disk that is ' - 'in a deep-sleep state.') - elif error.matches(udisks.Error.quark(), udisks.Error.DEVICE_BUSY): - message = _('Attempting to unmount a device that is busy.') - elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_CANCELLED): - message = _('The operation has already been cancelled.') - elif error.matches(udisks.Error.quark(), udisks.Error.NOT_AUTHORIZED) or \ - error.matches(udisks.Error.quark(), - udisks.Error.NOT_AUTHORIZED_CAN_OBTAIN) or \ - error.matches(udisks.Error.quark(), - udisks.Error.NOT_AUTHORIZED_DISMISSED): - message = _('Not authorized to perform the requested operation.') - elif error.matches(udisks.Error.quark(), udisks.Error.ALREADY_MOUNTED): - message = _('The device is already mounted.') - elif error.matches(udisks.Error.quark(), udisks.Error.NOT_MOUNTED): - message = _('The device is not mounted.') - elif error.matches(udisks.Error.quark(), - udisks.Error.OPTION_NOT_PERMITTED): - message = _('Not permitted to use the requested option.') - elif error.matches(udisks.Error.quark(), - udisks.Error.MOUNTED_BY_OTHER_USER): - message = _('The device is mounted by another user.') - else: - message = error.message - - return message diff --git a/plinth/modules/storage/views.py b/plinth/modules/storage/views.py index 87cdb8f09..75cd70579 100644 --- a/plinth/modules/storage/views.py +++ b/plinth/modules/storage/views.py @@ -34,7 +34,7 @@ from plinth.errors import PlinthError from plinth.modules import storage from plinth.utils import format_lazy -from . import udisks2, get_disk_info +from . import get_disk_info, get_error_message logger = logging.getLogger(__name__) @@ -53,7 +53,6 @@ def index(request): 'title': _('Storage'), 'description': storage.description, 'disks': disks, - 'devices': udisks2.list_devices(), 'manual_page': storage.manual_page, 'expandable_root_size': expandable_root_size }) @@ -133,7 +132,7 @@ def eject(request, device_path): messages.success(request, _('Device can be safely unplugged.')) except Exception as exception: try: - message = udisks2.get_error_message(exception) + message = get_error_message(exception) except AttributeError: message = str(exception)