mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
storage: Show notification when rootfs is read-only
Tests: - Change the partition to test to '/mnt'. Mount a loopback filesystem on /mnt. 'dd if=/dev/zero of=/test-file bs=1M count=100; mkfs.ext4 /test-file; mount -o loop /test-file /mnt'. Turn it to read-only with 'mount -o remount,ro /mnt'. Wait about 3 minutes for the notification to show up. - The notification shows icon, title and message as expected. The button power app appears and works as expected. - When the filesystem is mount rw again, the notification goes away in 3 minutes. 'mount -o remount,rw /mnt'. [sunil: Let glib.schedule decide time when debugging] [sunil: Perform exact matching in partition mount options] [sunil: Simplify notification message. Minor grammar change] [sunil: Minor refactoring for styling] Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
c8dac10308
commit
92492d2449
@ -68,6 +68,9 @@ class StorageApp(app_module.App):
|
||||
# Schedule initialization of UDisks2 initialization
|
||||
glib.schedule(3, udisks2.init, repeat=False)
|
||||
|
||||
# Check periodically for a read-only root filesystem
|
||||
glib.schedule(600, _warn_about_read_only_filesystem)
|
||||
|
||||
def setup(self, old_version):
|
||||
"""Install and configure the app."""
|
||||
super().setup(old_version)
|
||||
@ -271,7 +274,7 @@ def get_error_message(error):
|
||||
return message_map.get(short_error, error)
|
||||
|
||||
|
||||
def warn_about_low_disk_space(request):
|
||||
def warn_about_low_disk_space(_data):
|
||||
"""Warn about insufficient space on root partition."""
|
||||
from plinth.notification import Notification
|
||||
|
||||
@ -341,3 +344,55 @@ def report_failing_drive(id, is_failing):
|
||||
'type': 'dismiss'
|
||||
}], data=data, group='admin')
|
||||
note.dismiss(should_dismiss=not is_failing)
|
||||
|
||||
|
||||
def is_partition_read_only(mount_point='/'):
|
||||
"""Return True if the partition is mounted read-only."""
|
||||
for partition in psutil.disk_partitions(all=True):
|
||||
if partition.mountpoint == mount_point:
|
||||
return 'ro' in partition.opts.split(',')
|
||||
|
||||
raise ValueError('No such mount point')
|
||||
|
||||
|
||||
def _warn_about_read_only_filesystem(_data):
|
||||
"""Create a notification if the root filesystem is mounted read-only.
|
||||
|
||||
Remove the notification (if any) if the root filesystem is writable.
|
||||
"""
|
||||
from plinth.notification import Notification
|
||||
|
||||
show = is_partition_read_only()
|
||||
notification_id = 'storage-read-only-root-filesystem'
|
||||
|
||||
if not show:
|
||||
try:
|
||||
Notification.get(notification_id).delete()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return
|
||||
|
||||
message = gettext_noop(
|
||||
# xgettext:no-python-format
|
||||
'You cannot save configuration changes. Try rebooting the system. If '
|
||||
'the problem persists after a reboot, check the storage device for '
|
||||
'errors.')
|
||||
title = gettext_noop('Read-only root filesystem')
|
||||
data = {
|
||||
'app_icon': 'fa-hdd-o',
|
||||
'app_name': 'translate:' + gettext_noop('Storage'),
|
||||
}
|
||||
|
||||
# This is a serious issue so the notification can't be dismissed.
|
||||
actions = [{
|
||||
'type': 'link',
|
||||
'class': 'primary',
|
||||
'text': gettext_noop('Go to Power'),
|
||||
'url': 'power:index'
|
||||
}]
|
||||
|
||||
Notification.update_or_create(id=notification_id, app_id='storage',
|
||||
severity='error', title=title,
|
||||
message=message, actions=actions, data=data,
|
||||
group='admin')
|
||||
|
||||
@ -7,9 +7,12 @@ import contextlib
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
import psutil
|
||||
import pytest
|
||||
|
||||
from plinth.modules import storage
|
||||
from plinth.modules.storage import privileged
|
||||
|
||||
pytestmark = pytest.mark.usefixtures('mock_privileged')
|
||||
@ -343,3 +346,17 @@ def test_validate_directory_writable(path, error):
|
||||
def test_validate_directory_creatable(path, error):
|
||||
"""Test that directory creatable validation returns expected output."""
|
||||
_assert_validate_directory(path, error, check_creatable=True)
|
||||
|
||||
|
||||
@patch('psutil.disk_partitions')
|
||||
def test_is_partition_read_only(disk_partitions):
|
||||
"""Test whether checking for ro partition works."""
|
||||
partition = psutil._common.sdiskpart # pylint: disable=protected-access
|
||||
disk_partitions.return_value = [
|
||||
partition('/dev/root', '/', 'btrfs', 'rw,nosuid', 42, 42),
|
||||
partition('/dev/root', '/foo', 'btrfs', 'rw', 321, 321),
|
||||
partition('/dev/foo', '/bar', 'extfs', 'ro', 123, 123)
|
||||
]
|
||||
assert not storage.is_partition_read_only('/')
|
||||
assert not storage.is_partition_read_only('/foo')
|
||||
assert storage.is_partition_read_only('/bar')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user