Skip to content

Commit 9f5bebd

Browse files
authored
Mount falls back on restart if reload fails (#6197)
1 parent c712d3c commit 9f5bebd

File tree

3 files changed

+61
-16
lines changed

3 files changed

+61
-16
lines changed

supervisor/mounts/mount.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -343,25 +343,51 @@ async def reload(self) -> None:
343343
)
344344
await self.mount()
345345
except DBusError as err:
346-
raise MountError(
347-
f"Could not reload mount {self.name} due to: {err!s}", _LOGGER.error
348-
) from err
346+
_LOGGER.error(
347+
"Could not reload mount %s due to: %s. Trying a restart", self.name, err
348+
)
349+
await self._restart()
349350
else:
350351
if unit := await self._update_unit():
351352
await self._update_state_await(
352353
unit, not_state=UnitActiveState.ACTIVATING
353354
)
354355

355356
if not await self.is_mounted():
356-
raise MountActivationError(
357-
f"Reloading {self.name} did not succeed. Check host logs for errors from mount or systemd unit {self.unit_name} for details.",
358-
_LOGGER.error,
357+
_LOGGER.info(
358+
"Mount %s not correctly mounted after a reload. Trying a restart",
359+
self.name,
359360
)
361+
await self._restart()
360362

361363
# If it is mounted now, dismiss corresponding issue if present
362364
if self.failed_issue in self.sys_resolution.issues:
363365
self.sys_resolution.dismiss_issue(self.failed_issue)
364366

367+
async def _restart(self) -> None:
368+
"""Restart mount unit to re-mount."""
369+
try:
370+
await self.sys_dbus.systemd.restart_unit(self.unit_name, StartUnitMode.FAIL)
371+
except DBusSystemdNoSuchUnit:
372+
_LOGGER.info(
373+
"Mount %s is not mounted, mounting instead of restarting", self.name
374+
)
375+
await self.mount()
376+
return
377+
except DBusError as err:
378+
raise MountError(
379+
f"Could not restart mount {self.name} due to: {err!s}", _LOGGER.error
380+
) from err
381+
382+
if unit := await self._update_unit():
383+
await self._update_state_await(unit, not_state=UnitActiveState.ACTIVATING)
384+
385+
if not await self.is_mounted():
386+
raise MountActivationError(
387+
f"Restarting {self.name} did not succeed. Check host logs for errors from mount or systemd unit {self.unit_name} for details.",
388+
_LOGGER.error,
389+
)
390+
365391

366392
class NetworkMount(Mount, ABC):
367393
"""A network mount."""

tests/dbus_service_mocks/systemd.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Systemd(DBusServiceMock):
3636
response_reload_or_restart_unit: str | DBusError = (
3737
"/org/freedesktop/systemd1/job/7623"
3838
)
39+
response_restart_unit: str | DBusError = "/org/freedesktop/systemd1/job/7623"
3940
response_start_transient_unit: str | DBusError = (
4041
"/org/freedesktop/systemd1/job/7623"
4142
)
@@ -683,9 +684,11 @@ def ReloadOrRestartUnit(self, name: "s", mode: "s") -> "o":
683684
@dbus_method()
684685
def RestartUnit(self, name: "s", mode: "s") -> "o":
685686
"""Restart a service unit."""
687+
if isinstance(self.response_restart_unit, DBusError):
688+
raise self.response_restart_unit # pylint: disable=raising-bad-type
686689
if self.mock_systemd_unit:
687690
self.mock_systemd_unit.active_state = "active"
688-
return "/org/freedesktop/systemd1/job/7623"
691+
return self.response_restart_unit
689692

690693
@dbus_method()
691694
def StartTransientUnit(

tests/mounts/test_mount.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pathlib import Path
88
import stat
99
from typing import Any
10-
from unittest.mock import MagicMock
10+
from unittest.mock import MagicMock, patch
1111

1212
from dbus_fast import DBusError, ErrorType, Variant
1313
import pytest
@@ -514,18 +514,16 @@ async def test_unmount_failure(
514514
assert systemd_service.StopUnit.calls == []
515515

516516

517+
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern", "mock_is_mount")
517518
async def test_reload_failure(
518-
coresys: CoreSys,
519-
all_dbus_services: dict[str, DBusServiceMock],
520-
tmp_supervisor_data,
521-
path_extern,
522-
mock_is_mount,
519+
coresys: CoreSys, all_dbus_services: dict[str, DBusServiceMock]
523520
):
524521
"""Test failure to reload."""
525522
systemd_service: SystemdService = all_dbus_services["systemd"]
526523
systemd_unit_service: SystemdUnitService = all_dbus_services["systemd_unit"]
527524
systemd_service.StartTransientUnit.calls.clear()
528525
systemd_service.ReloadOrRestartUnit.calls.clear()
526+
systemd_service.RestartUnit.calls.clear()
529527
systemd_service.GetUnit.calls.clear()
530528

531529
mount = Mount.from_dict(
@@ -539,28 +537,46 @@ async def test_reload_failure(
539537
},
540538
)
541539

542-
# Raise error on ReloadOrRestartUnit error
540+
# Raise error on ReloadOrRestartUnit and RestartUnit error
543541
systemd_service.response_reload_or_restart_unit = ERROR_FAILURE
542+
systemd_service.response_restart_unit = ERROR_FAILURE
544543
with pytest.raises(MountError):
545544
await mount.reload()
546545

547546
assert mount.state is None
548547
assert len(systemd_service.ReloadOrRestartUnit.calls) == 1
548+
assert len(systemd_service.RestartUnit.calls) == 1
549549
assert systemd_service.GetUnit.calls == []
550550
assert systemd_service.StartTransientUnit.calls == []
551551

552-
# Raise error if state is not "active" after reload
552+
# RestartUnit if ReloadOrRestartUnit does not get it mounted
553553
systemd_service.ReloadOrRestartUnit.calls.clear()
554+
systemd_service.RestartUnit.calls.clear()
554555
systemd_service.response_reload_or_restart_unit = (
555556
"/org/freedesktop/systemd1/job/7623"
556557
)
558+
systemd_service.response_restart_unit = "/org/freedesktop/systemd1/job/7623"
559+
with patch("supervisor.mounts.mount.Path.is_mount", side_effect=[False, True]):
560+
await mount.reload()
561+
562+
assert mount.state == UnitActiveState.ACTIVE
563+
assert len(systemd_service.ReloadOrRestartUnit.calls) == 1
564+
assert len(systemd_service.RestartUnit.calls) == 1
565+
assert len(systemd_service.GetUnit.calls) == 2
566+
assert systemd_service.StartTransientUnit.calls == []
567+
568+
# Raise error if state is not "active" after reload
569+
systemd_service.ReloadOrRestartUnit.calls.clear()
570+
systemd_service.RestartUnit.calls.clear()
571+
systemd_service.GetUnit.calls.clear()
557572
systemd_unit_service.active_state = "failed"
558573
with pytest.raises(MountError):
559574
await mount.reload()
560575

561576
assert mount.state == UnitActiveState.FAILED
562577
assert len(systemd_service.ReloadOrRestartUnit.calls) == 1
563-
assert len(systemd_service.GetUnit.calls) == 1
578+
assert len(systemd_service.RestartUnit.calls) == 1
579+
assert len(systemd_service.GetUnit.calls) == 2
564580
assert systemd_service.StartTransientUnit.calls == []
565581

566582
# If error is NoSuchUnit then don't raise just mount instead as its not mounted

0 commit comments

Comments
 (0)