diff --git a/supervisor/dbus/agent/__init__.py b/supervisor/dbus/agent/__init__.py index 532d0ed5f..4cbff54d3 100644 --- a/supervisor/dbus/agent/__init__.py +++ b/supervisor/dbus/agent/__init__.py @@ -6,7 +6,7 @@ from typing import Any from awesomeversion import AwesomeVersion from dbus_fast.aio.message_bus import MessageBus -from ...exceptions import DBusError, DBusInterfaceError +from ...exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from ..const import ( DBUS_ATTR_DIAGNOSTICS, DBUS_ATTR_VERSION, @@ -99,7 +99,7 @@ class OSAgent(DBusInterfaceProxy): await asyncio.gather(*[dbus.connect(bus) for dbus in self.all]) except DBusError: _LOGGER.warning("Can't connect to OS-Agent") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No OS-Agent support on the host. Some Host functions have been disabled." ) diff --git a/supervisor/dbus/hostname.py b/supervisor/dbus/hostname.py index b0889efec..8327df63a 100644 --- a/supervisor/dbus/hostname.py +++ b/supervisor/dbus/hostname.py @@ -3,7 +3,7 @@ import logging from dbus_fast.aio.message_bus import MessageBus -from ..exceptions import DBusError, DBusInterfaceError +from ..exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from .const import ( DBUS_ATTR_CHASSIS, DBUS_ATTR_DEPLOYMENT, @@ -39,7 +39,7 @@ class Hostname(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to systemd-hostname") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No hostname support on the host. Hostname functions have been disabled." ) diff --git a/supervisor/dbus/logind.py b/supervisor/dbus/logind.py index 2c6f315ad..52c12fbbb 100644 --- a/supervisor/dbus/logind.py +++ b/supervisor/dbus/logind.py @@ -3,7 +3,7 @@ import logging from dbus_fast.aio.message_bus import MessageBus -from ..exceptions import DBusError, DBusInterfaceError +from ..exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from .const import DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND from .interface import DBusInterface from .utils import dbus_connected @@ -28,8 +28,8 @@ class Logind(DBusInterface): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to systemd-logind") - except DBusInterfaceError: - _LOGGER.info("No systemd-logind support on the host.") + except (DBusServiceUnkownError, DBusInterfaceError): + _LOGGER.warning("No systemd-logind support on the host.") @dbus_connected async def reboot(self) -> None: diff --git a/supervisor/dbus/network/__init__.py b/supervisor/dbus/network/__init__.py index 365aca25b..08a535a51 100644 --- a/supervisor/dbus/network/__init__.py +++ b/supervisor/dbus/network/__init__.py @@ -9,6 +9,8 @@ from ...exceptions import ( DBusError, DBusFatalError, DBusInterfaceError, + DBusNoReplyError, + DBusServiceUnkownError, HostNotSupportedError, NetworkInterfaceNotFound, ) @@ -143,7 +145,7 @@ class NetworkManager(DBusInterfaceProxy): await self.settings.connect(bus) except DBusError: _LOGGER.warning("Can't connect to Network Manager") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No Network Manager support on the host. Local network functions have been disabled." ) @@ -210,8 +212,22 @@ class NetworkManager(DBusInterfaceProxy): # try to query it. Ignore those cases. _LOGGER.debug("Can't process %s: %s", device, err) continue + except ( + DBusNoReplyError, + DBusServiceUnkownError, + ) as err: + # This typically means that NetworkManager disappeared. Give up immeaditly. + _LOGGER.error( + "NetworkManager not responding while processing %s: %s. Giving up.", + device, + err, + ) + capture_exception(err) + return except Exception as err: # pylint: disable=broad-except - _LOGGER.exception("Error while processing %s: %s", device, err) + _LOGGER.exception( + "Unkown error while processing %s: %s", device, err + ) capture_exception(err) continue diff --git a/supervisor/dbus/network/dns.py b/supervisor/dbus/network/dns.py index b414b4813..82ae3583e 100644 --- a/supervisor/dbus/network/dns.py +++ b/supervisor/dbus/network/dns.py @@ -12,7 +12,7 @@ from ...const import ( ATTR_PRIORITY, ATTR_VPN, ) -from ...exceptions import DBusError, DBusInterfaceError +from ...exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from ..const import ( DBUS_ATTR_CONFIGURATION, DBUS_ATTR_MODE, @@ -67,7 +67,7 @@ class NetworkManagerDNS(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to DnsManager") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No DnsManager support on the host. Local DNS functions have been disabled." ) diff --git a/supervisor/dbus/network/settings.py b/supervisor/dbus/network/settings.py index bba1f1176..d3402e0a2 100644 --- a/supervisor/dbus/network/settings.py +++ b/supervisor/dbus/network/settings.py @@ -4,7 +4,7 @@ from typing import Any from dbus_fast.aio.message_bus import MessageBus -from ...exceptions import DBusError, DBusInterfaceError +from ...exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from ..const import DBUS_NAME_NM, DBUS_OBJECT_SETTINGS from ..interface import DBusInterface from ..network.setting import NetworkSetting @@ -28,7 +28,7 @@ class NetworkManagerSettings(DBusInterface): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to Network Manager Settings") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No Network Manager Settings support on the host. Local network functions have been disabled." ) diff --git a/supervisor/dbus/rauc.py b/supervisor/dbus/rauc.py index 3c6496b90..fe236e499 100644 --- a/supervisor/dbus/rauc.py +++ b/supervisor/dbus/rauc.py @@ -4,7 +4,7 @@ from typing import Any from dbus_fast.aio.message_bus import MessageBus -from ..exceptions import DBusError, DBusInterfaceError +from ..exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from ..utils.dbus import DBusSignalWrapper from .const import ( DBUS_ATTR_BOOT_SLOT, @@ -49,7 +49,7 @@ class Rauc(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to rauc") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning("Host has no rauc support. OTA updates have been disabled.") @property diff --git a/supervisor/dbus/resolved.py b/supervisor/dbus/resolved.py index 89c387df6..fdca304c0 100644 --- a/supervisor/dbus/resolved.py +++ b/supervisor/dbus/resolved.py @@ -5,7 +5,7 @@ import logging from dbus_fast.aio.message_bus import MessageBus -from ..exceptions import DBusError, DBusInterfaceError +from ..exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from .const import ( DBUS_ATTR_CACHE_STATISTICS, DBUS_ATTR_CURRENT_DNS_SERVER, @@ -59,7 +59,7 @@ class Resolved(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to systemd-resolved.") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "Host has no systemd-resolved support. DNS will not work correctly." ) diff --git a/supervisor/dbus/systemd.py b/supervisor/dbus/systemd.py index b1bb0341b..6475029ed 100644 --- a/supervisor/dbus/systemd.py +++ b/supervisor/dbus/systemd.py @@ -10,6 +10,7 @@ from ..exceptions import ( DBusError, DBusFatalError, DBusInterfaceError, + DBusServiceUnkownError, DBusSystemdNoSuchUnit, ) from .const import ( @@ -86,7 +87,7 @@ class Systemd(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to systemd") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No systemd support on the host. Host control has been disabled." ) diff --git a/supervisor/dbus/timedate.py b/supervisor/dbus/timedate.py index f940aedc4..731bd56c1 100644 --- a/supervisor/dbus/timedate.py +++ b/supervisor/dbus/timedate.py @@ -4,7 +4,7 @@ import logging from dbus_fast.aio.message_bus import MessageBus -from ..exceptions import DBusError, DBusInterfaceError +from ..exceptions import DBusError, DBusInterfaceError, DBusServiceUnkownError from ..utils.dt import utc_from_timestamp from .const import ( DBUS_ATTR_NTP, @@ -63,7 +63,7 @@ class TimeDate(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to systemd-timedate") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No timedate support on the host. Time/Date functions have been disabled." ) diff --git a/supervisor/dbus/udisks2/__init__.py b/supervisor/dbus/udisks2/__init__.py index e8e49c8cd..c5b17ef7d 100644 --- a/supervisor/dbus/udisks2/__init__.py +++ b/supervisor/dbus/udisks2/__init__.py @@ -6,7 +6,12 @@ from typing import Any from awesomeversion import AwesomeVersion from dbus_fast.aio import MessageBus -from ...exceptions import DBusError, DBusInterfaceError, DBusObjectError +from ...exceptions import ( + DBusError, + DBusInterfaceError, + DBusObjectError, + DBusServiceUnkownError, +) from ..const import ( DBUS_ATTR_SUPPORTED_FILESYSTEMS, DBUS_ATTR_VERSION, @@ -45,7 +50,7 @@ class UDisks2(DBusInterfaceProxy): await super().connect(bus) except DBusError: _LOGGER.warning("Can't connect to udisks2") - except DBusInterfaceError: + except (DBusServiceUnkownError, DBusInterfaceError): _LOGGER.warning( "No udisks2 support on the host. Host control has been disabled." ) diff --git a/supervisor/exceptions.py b/supervisor/exceptions.py index a24242c0b..65fb4972b 100644 --- a/supervisor/exceptions.py +++ b/supervisor/exceptions.py @@ -335,6 +335,10 @@ class DBusNotConnectedError(HostNotSupportedError): """D-Bus is not connected and call a method.""" +class DBusServiceUnkownError(HassioNotSupportedError): + """D-Bus service was not available.""" + + class DBusInterfaceError(HassioNotSupportedError): """D-Bus interface not connected.""" @@ -363,6 +367,10 @@ class DBusTimeoutError(DBusError): """D-Bus call timed out.""" +class DBusNoReplyError(DBusError): + """D-Bus remote didn't reply/disconnected.""" + + class DBusFatalError(DBusError): """D-Bus call going wrong. diff --git a/supervisor/utils/dbus.py b/supervisor/utils/dbus.py index 864b61079..06fd762c6 100644 --- a/supervisor/utils/dbus.py +++ b/supervisor/utils/dbus.py @@ -15,18 +15,21 @@ from dbus_fast import ( ) from dbus_fast.aio.message_bus import MessageBus from dbus_fast.aio.proxy_object import ProxyInterface, ProxyObject -from dbus_fast.errors import DBusError +from dbus_fast.errors import DBusError as DBusFastDBusError from dbus_fast.introspection import Node from ..exceptions import ( + DBusError, DBusFatalError, DBusInterfaceError, DBusInterfaceMethodError, DBusInterfacePropertyError, DBusInterfaceSignalError, + DBusNoReplyError, DBusNotConnectedError, DBusObjectError, DBusParseError, + DBusServiceUnkownError, DBusTimeoutError, HassioNotSupportedError, ) @@ -62,9 +65,11 @@ class DBus: return self @staticmethod - def from_dbus_error(err: DBusError) -> HassioNotSupportedError | DBusError: + def from_dbus_error(err: DBusFastDBusError) -> HassioNotSupportedError | DBusError: """Return correct dbus error based on type.""" - if err.type in {ErrorType.SERVICE_UNKNOWN, ErrorType.UNKNOWN_INTERFACE}: + if err.type == ErrorType.SERVICE_UNKNOWN: + return DBusServiceUnkownError(err.text) + if err.type == ErrorType.UNKNOWN_INTERFACE: return DBusInterfaceError(err.text) if err.type in { ErrorType.UNKNOWN_METHOD, @@ -80,6 +85,8 @@ class DBus: return DBusNotConnectedError(err.text) if err.type == ErrorType.TIMEOUT: return DBusTimeoutError(err.text) + if err.type == ErrorType.NO_REPLY: + return DBusNoReplyError(err.text) return DBusFatalError(err.text, type_=err.type) @staticmethod @@ -102,7 +109,7 @@ class DBus: *args, unpack_variants=True ) return await getattr(proxy_interface, method)(*args) - except DBusError as err: + except DBusFastDBusError as err: raise DBus.from_dbus_error(err) except Exception as err: # pylint: disable=broad-except capture_exception(err) @@ -126,6 +133,8 @@ class DBus: raise DBusParseError( f"Can't parse introspect data: {err}", _LOGGER.error ) from err + except DBusFastDBusError as err: + raise DBus.from_dbus_error(err) except (EOFError, TimeoutError): _LOGGER.warning( "Busy system at %s - %s", self.bus_name, self.object_path diff --git a/tests/dbus/agent/test_agent.py b/tests/dbus/agent/test_agent.py index 63c064d73..bc05214ca 100644 --- a/tests/dbus/agent/test_agent.py +++ b/tests/dbus/agent/test_agent.py @@ -5,11 +5,12 @@ import pytest from supervisor.dbus.agent import OSAgent +from tests.common import mock_dbus_services from tests.dbus_service_mocks.base import DBusServiceMock from tests.dbus_service_mocks.os_agent import OSAgent as OSAgentService -@pytest.fixture(name="os_agent_service", autouse=True) +@pytest.fixture(name="os_agent_service") async def fixture_os_agent_service( os_agent_services: dict[str, DBusServiceMock] ) -> OSAgentService: @@ -39,3 +40,36 @@ async def test_dbus_osagent( await os_agent_service.ping() await os_agent_service.ping() assert os_agent.diagnostics is True + + +@pytest.mark.parametrize( + "skip_service", + [ + "os_agent", + "agent_apparmor", + "agent_datadisk", + ], +) +async def test_dbus_osagent_connect_error( + skip_service: str, dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test OS Agent errors during connect.""" + os_agent_services = { + "os_agent": None, + "agent_apparmor": None, + "agent_cgroup": None, + "agent_datadisk": None, + "agent_system": None, + "agent_boards": None, + "agent_boards_yellow": None, + } + os_agent_services.pop(skip_service) + await mock_dbus_services( + os_agent_services, + dbus_session_bus, + ) + + os_agent = OSAgent() + await os_agent.connect(dbus_session_bus) + + assert "No OS-Agent support on the host" in caplog.text diff --git a/tests/dbus/network/test_dns.py b/tests/dbus/network/test_dns.py index 8fe3cd4d7..038b9be32 100644 --- a/tests/dbus/network/test_dns.py +++ b/tests/dbus/network/test_dns.py @@ -12,7 +12,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.network_dns_manager import DnsManager as DnsManagerService -@pytest.fixture(name="dns_manager_service", autouse=True) +@pytest.fixture(name="dns_manager_service") async def fixture_dns_manager_service( dbus_session_bus: MessageBus, ) -> DnsManagerService: @@ -49,3 +49,12 @@ async def test_dns( await dns_manager_service.ping() await dns_manager_service.ping() assert dns_manager.mode == "default" + + +async def test_dbus_dns_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to dns error.""" + dns_manager = NetworkManagerDNS() + await dns_manager.connect(dbus_session_bus) + assert "No DnsManager support on the host" in caplog.text diff --git a/tests/dbus/network/test_network_manager.py b/tests/dbus/network/test_network_manager.py index 31a7cfd08..a71239311 100644 --- a/tests/dbus/network/test_network_manager.py +++ b/tests/dbus/network/test_network_manager.py @@ -9,7 +9,12 @@ import pytest from supervisor.dbus.const import ConnectionStateType from supervisor.dbus.network import NetworkManager from supervisor.dbus.network.interface import NetworkInterface -from supervisor.exceptions import DBusFatalError, DBusParseError, HostNotSupportedError +from supervisor.exceptions import ( + DBusFatalError, + DBusParseError, + DBusServiceUnkownError, + HostNotSupportedError, +) from supervisor.utils.dbus import DBus from tests.const import TEST_INTERFACE, TEST_INTERFACE_WLAN @@ -20,7 +25,7 @@ from tests.dbus_service_mocks.network_manager import ( ) -@pytest.fixture(name="network_manager_service", autouse=True) +@pytest.fixture(name="network_manager_service") async def fixture_network_manager_service( network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], ) -> NetworkManagerService: @@ -134,6 +139,7 @@ async def test_removed_devices_disconnect( async def test_handling_bad_devices( + network_manager_service: NetworkManagerService, network_manager: NetworkManager, caplog: pytest.LogCaptureFixture, capture_exception: Mock, @@ -161,7 +167,7 @@ async def test_handling_bad_devices( await network_manager.update( {"Devices": [device := "/org/freedesktop/NetworkManager/Devices/102"]} ) - assert f"Error while processing {device}" in caplog.text + assert f"Unkown error while processing {device}" in caplog.text capture_exception.assert_called_once_with(err) # We should be able to debug these situations if necessary @@ -207,3 +213,37 @@ async def test_ignore_veth_only_changes( ) await network_manager_service.ping() connect.assert_called_once() + + +async def test_network_manager_stopped( + network_manager_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]], + network_manager: NetworkManager, + dbus_session_bus: MessageBus, + caplog: pytest.LogCaptureFixture, + capture_exception: Mock, +): + """Test network manager stopped and dbus service no longer accessible.""" + services = list(network_manager_services.values()) + while services: + service = services.pop(0) + if isinstance(service, dict): + services.extend(service.values()) + else: + dbus_session_bus.unexport(service.object_path, service) + await dbus_session_bus.release_name("org.freedesktop.NetworkManager") + + assert network_manager.is_connected is True + await network_manager.update( + { + "Devices": [ + "/org/freedesktop/NetworkManager/Devices/9", + "/org/freedesktop/NetworkManager/Devices/15", + "/org/freedesktop/NetworkManager/Devices/20", + "/org/freedesktop/NetworkManager/Devices/35", + ] + } + ) + + capture_exception.assert_called_once() + assert isinstance(capture_exception.call_args.args[0], DBusServiceUnkownError) + assert "NetworkManager not responding" in caplog.text diff --git a/tests/dbus/network/test_settings.py b/tests/dbus/network/test_settings.py index 2a65bf22b..7df8c5e13 100644 --- a/tests/dbus/network/test_settings.py +++ b/tests/dbus/network/test_settings.py @@ -11,7 +11,7 @@ from tests.dbus_service_mocks.network_connection_settings import SETTINGS_FIXTUR from tests.dbus_service_mocks.network_settings import Settings as SettingsService -@pytest.fixture(name="settings_service", autouse=True) +@pytest.fixture(name="settings_service") async def fixture_settings_service(dbus_session_bus: MessageBus) -> SettingsService: """Mock Settings service.""" yield ( @@ -55,3 +55,12 @@ async def test_reload_connections( assert await settings.reload_connections() is True assert settings_service.ReloadConnections.calls == [tuple()] + + +async def test_dbus_network_settings_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to network settings error.""" + settings = NetworkManagerSettings() + await settings.connect(dbus_session_bus) + assert "No Network Manager Settings support on the host" in caplog.text diff --git a/tests/dbus/test_hostname.py b/tests/dbus/test_hostname.py index 5fe8cb45c..4563a38dc 100644 --- a/tests/dbus/test_hostname.py +++ b/tests/dbus/test_hostname.py @@ -10,7 +10,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.hostname import Hostname as HostnameService -@pytest.fixture(name="hostname_service", autouse=True) +@pytest.fixture(name="hostname_service") async def fixture_hostname_service(dbus_session_bus: MessageBus) -> HostnameService: """Mock hostname dbus service.""" yield (await mock_dbus_services({"hostname": None}, dbus_session_bus))["hostname"] @@ -61,3 +61,12 @@ async def test_dbus_sethostname( assert hostname_service.SetStaticHostname.calls == [("StarWars", False)] await hostname_service.ping() assert hostname.hostname == "StarWars" + + +async def test_dbus_hostname_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to hostname error.""" + hostname = Hostname() + await hostname.connect(dbus_session_bus) + assert "No hostname support on the host" in caplog.text diff --git a/tests/dbus/test_login.py b/tests/dbus/test_login.py index 8e40f4faf..d53df0027 100644 --- a/tests/dbus/test_login.py +++ b/tests/dbus/test_login.py @@ -10,7 +10,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.logind import Logind as LogindService -@pytest.fixture(name="logind_service", autouse=True) +@pytest.fixture(name="logind_service") async def fixture_logind_service(dbus_session_bus: MessageBus) -> LogindService: """Mock logind dbus service.""" yield (await mock_dbus_services({"logind": None}, dbus_session_bus))["logind"] @@ -42,3 +42,12 @@ async def test_power_off(logind_service: LogindService, dbus_session_bus: Messag assert await logind.power_off() is None assert logind_service.PowerOff.calls == [(False,)] + + +async def test_dbus_logind_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to logind error.""" + logind = Logind() + await logind.connect(dbus_session_bus) + assert "No systemd-logind support on the host" in caplog.text diff --git a/tests/dbus/test_rauc.py b/tests/dbus/test_rauc.py index 8cb794def..312ae87d3 100644 --- a/tests/dbus/test_rauc.py +++ b/tests/dbus/test_rauc.py @@ -11,7 +11,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.rauc import Rauc as RaucService -@pytest.fixture(name="rauc_service", autouse=True) +@pytest.fixture(name="rauc_service") async def fixture_rauc_service(dbus_session_bus: MessageBus) -> RaucService: """Mock rauc dbus service.""" yield (await mock_dbus_services({"rauc": None}, dbus_session_bus))["rauc"] @@ -41,7 +41,7 @@ async def test_rauc_info(rauc_service: RaucService, dbus_session_bus: MessageBus assert rauc.last_error == "" -async def test_install(dbus_session_bus: MessageBus): +async def test_install(rauc_service: RaucService, dbus_session_bus: MessageBus): """Test install.""" rauc = Rauc() @@ -55,7 +55,7 @@ async def test_install(dbus_session_bus: MessageBus): assert await signal.wait_for_signal() == [0] -async def test_get_slot_status(dbus_session_bus: MessageBus): +async def test_get_slot_status(rauc_service: RaucService, dbus_session_bus: MessageBus): """Test get slot status.""" rauc = Rauc() @@ -76,7 +76,7 @@ async def test_get_slot_status(dbus_session_bus: MessageBus): assert slot_status[4][1]["bootname"] == "B" -async def test_mark(dbus_session_bus: MessageBus): +async def test_mark(rauc_service: RaucService, dbus_session_bus: MessageBus): """Test mark.""" rauc = Rauc() @@ -88,3 +88,12 @@ async def test_mark(dbus_session_bus: MessageBus): mark = await rauc.mark(RaucState.GOOD, "booted") assert mark[0] == "kernel.1" assert mark[1] == "marked slot kernel.1 as good" + + +async def test_dbus_rauc_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to rauc error.""" + rauc = Rauc() + await rauc.connect(dbus_session_bus) + assert "Host has no rauc support" in caplog.text diff --git a/tests/dbus/test_resolved.py b/tests/dbus/test_resolved.py index cdde733db..93681025f 100644 --- a/tests/dbus/test_resolved.py +++ b/tests/dbus/test_resolved.py @@ -19,7 +19,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.resolved import Resolved as ResolvedService -@pytest.fixture(name="resolved_service", autouse=True) +@pytest.fixture(name="resolved_service") async def fixture_resolved_service(dbus_session_bus: MessageBus) -> ResolvedService: """Mock resolved dbus service.""" yield (await mock_dbus_services({"resolved": None}, dbus_session_bus))["resolved"] @@ -102,3 +102,12 @@ async def test_dbus_resolved_info( await resolved_service.ping() await resolved_service.ping() # To process the follow-up get all properties call assert resolved.llmnr_hostname == "homeassistant" + + +async def test_dbus_resolved_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to resolved error.""" + resolved = Resolved() + await resolved.connect(dbus_session_bus) + assert "Host has no systemd-resolved support" in caplog.text diff --git a/tests/dbus/test_timedate.py b/tests/dbus/test_timedate.py index 6d8d58559..75957f76d 100644 --- a/tests/dbus/test_timedate.py +++ b/tests/dbus/test_timedate.py @@ -12,7 +12,7 @@ from tests.common import mock_dbus_services from tests.dbus_service_mocks.timedate import TimeDate as TimeDateService -@pytest.fixture(name="timedate_service", autouse=True) +@pytest.fixture(name="timedate_service") async def fixture_timedate_service(dbus_session_bus: MessageBus) -> TimeDateService: """Mock timedate dbus service.""" yield (await mock_dbus_services({"timedate": None}, dbus_session_bus))["timedate"] @@ -81,3 +81,12 @@ async def test_dbus_setntp( assert timedate_service.SetNTP.calls == [(False, False)] await timedate_service.ping() assert timedate.ntp is False + + +async def test_dbus_timedate_connect_error( + dbus_session_bus: MessageBus, caplog: pytest.LogCaptureFixture +): + """Test connecting to timedate error.""" + timedate = TimeDate() + await timedate.connect(dbus_session_bus) + assert "No timedate support on the host" in caplog.text diff --git a/tests/utils/test_dbus.py b/tests/utils/test_dbus.py index d34e536dc..e7c761d48 100644 --- a/tests/utils/test_dbus.py +++ b/tests/utils/test_dbus.py @@ -2,12 +2,18 @@ from unittest.mock import AsyncMock, Mock, patch +from dbus_fast import ErrorType from dbus_fast.aio.message_bus import MessageBus +from dbus_fast.errors import DBusError as DBusFastDBusError from dbus_fast.service import method, signal import pytest from supervisor.dbus.const import DBUS_OBJECT_BASE -from supervisor.exceptions import DBusFatalError, DBusInterfaceError +from supervisor.exceptions import ( + DBusFatalError, + DBusInterfaceError, + DBusServiceUnkownError, +) from supervisor.utils.dbus import DBus from tests.common import load_fixture @@ -172,3 +178,12 @@ async def test_init_proxy(test_service: TestInterface, dbus_session_bus: Message test_service.signal_test() await test_service.ping() assert callback_count == 0 + + +def test_from_dbus_error(): + """Test converting DBus fast errors to Supervisor specific errors.""" + dbus_fast_error = DBusFastDBusError( + ErrorType.SERVICE_UNKNOWN, "The name is not activatable" + ) + + assert type(DBus.from_dbus_error(dbus_fast_error)) is DBusServiceUnkownError