Share MessageBus across dbus objects (#3860)

* Share MessageBus across dbus objects

* Fix connect calls in tests
This commit is contained in:
Mike Degatano 2022-09-12 14:10:12 -04:00 committed by GitHub
parent 4f272ad4fd
commit b71c6c60da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 222 additions and 136 deletions

View File

@ -298,6 +298,7 @@ class Core(CoreSysAttributes):
self.sys_websession.close(),
self.sys_ingress.unload(),
self.sys_hardware.unload(),
self.sys_dbus.unload(),
]
)
except asyncio.TimeoutError:

View File

@ -4,6 +4,7 @@ import logging
from typing import Any
from awesomeversion import AwesomeVersion
from dbus_next.aio.message_bus import MessageBus
from ...exceptions import DBusError, DBusInterfaceError
from ...utils.dbus import DBus
@ -78,14 +79,14 @@ class OSAgent(DBusInterface):
self.dbus.set_property(DBUS_IFACE_HAOS, DBUS_ATTR_DIAGNOSTICS, value)
)
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS)
await self.cgroup.connect()
await self.apparmor.connect()
await self.system.connect()
await self.datadisk.connect()
self.dbus = await DBus.connect(bus, DBUS_NAME_HAOS, DBUS_OBJECT_HAOS)
await self.cgroup.connect(bus)
await self.apparmor.connect(bus)
await self.system.connect(bus)
await self.datadisk.connect(bus)
except DBusError:
_LOGGER.warning("Can't connect to OS-Agent")
except DBusInterfaceError:

View File

@ -3,6 +3,7 @@ from pathlib import Path
from typing import Any
from awesomeversion import AwesomeVersion
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import (
@ -28,9 +29,9 @@ class AppArmor(DBusInterface):
"""Return version of host AppArmor parser."""
return AwesomeVersion(self.properties[DBUS_ATTR_PARSER_VERSION])
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_APPARMOR)
self.dbus = await DBus.connect(bus, DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_APPARMOR)
@dbus_connected
async def update(self):

View File

@ -1,5 +1,7 @@
"""CGroup object for OS-Agent."""
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_CGROUP
from ..interface import DBusInterface
@ -9,9 +11,9 @@ from ..utils import dbus_connected
class CGroup(DBusInterface):
"""CGroup object for OS Agent."""
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_CGROUP)
self.dbus = await DBus.connect(bus, DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_CGROUP)
@dbus_connected
async def add_devices_allowed(self, container_id: str, permission: str) -> None:

View File

@ -2,6 +2,8 @@
from pathlib import Path
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import (
DBUS_ATTR_CURRENT_DEVICE,
@ -26,9 +28,9 @@ class DataDisk(DBusInterface):
"""Return current device used for data."""
return Path(self.properties[DBUS_ATTR_CURRENT_DEVICE])
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_DATADISK)
self.dbus = await DBus.connect(bus, DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_DATADISK)
@dbus_connected
async def update(self):

View File

@ -1,5 +1,7 @@
"""System object for OS-Agent."""
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_SYSTEM
from ..interface import DBusInterface
@ -9,9 +11,9 @@ from ..utils import dbus_connected
class System(DBusInterface):
"""System object for OS Agent."""
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_SYSTEM)
self.dbus = await DBus.connect(bus, DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_SYSTEM)
@dbus_connected
async def schedule_wipe_device(self) -> None:

View File

@ -2,6 +2,8 @@
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from .const import (
@ -33,10 +35,12 @@ class Hostname(DBusInterface):
"""Initialize Properties."""
self.properties: dict[str, Any] = {}
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_HOSTNAME, DBUS_OBJECT_HOSTNAME)
self.dbus = await DBus.connect(
bus, DBUS_NAME_HOSTNAME, DBUS_OBJECT_HOSTNAME
)
except DBusError:
_LOGGER.warning("Can't connect to systemd-hostname")
except DBusInterfaceError:

View File

@ -3,6 +3,8 @@ from abc import ABC, abstractmethod
from functools import wraps
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..utils.dbus import DBus
@ -31,7 +33,7 @@ class DBusInterface(ABC):
return self.dbus is not None
@abstractmethod
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
def disconnect(self):
@ -47,5 +49,5 @@ class DBusInterfaceProxy(ABC):
properties: dict[str, Any] | None = None
@abstractmethod
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""

View File

@ -1,6 +1,8 @@
"""Interface to Logind over D-Bus."""
import logging
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from .const import DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND
@ -18,10 +20,10 @@ class Logind(DBusInterface):
name = DBUS_NAME_LOGIND
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND)
self.dbus = await DBus.connect(bus, DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND)
except DBusError:
_LOGGER.warning("Can't connect to systemd-logind")
except DBusInterfaceError:

View File

@ -1,8 +1,12 @@
"""D-Bus interface objects."""
import logging
from dbus_next import BusType
from dbus_next.aio.message_bus import MessageBus
from ..const import SOCKET_DBUS
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import DBusFatalError
from .agent import OSAgent
from .hostname import Hostname
from .interface import DBusInterface
@ -31,6 +35,7 @@ class DBusManager(CoreSysAttributes):
self._agent: OSAgent = OSAgent()
self._timedate: TimeDate = TimeDate()
self._resolved: Resolved = Resolved()
self._bus: MessageBus | None = None
@property
def systemd(self) -> Systemd:
@ -72,6 +77,11 @@ class DBusManager(CoreSysAttributes):
"""Return the resolved interface."""
return self._resolved
@property
def bus(self) -> MessageBus | None:
"""Return the message bus."""
return self._bus
async def load(self) -> None:
"""Connect interfaces to D-Bus."""
if not SOCKET_DBUS.exists():
@ -80,6 +90,15 @@ class DBusManager(CoreSysAttributes):
)
return
try:
self._bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
except Exception as err:
raise DBusFatalError(
"Cannot connect to system D-Bus. Disabled any kind of host control!"
) from err
_LOGGER.info("Connected to system D-Bus.")
dbus_loads: list[DBusInterface] = [
self.agent,
self.systemd,
@ -93,8 +112,13 @@ class DBusManager(CoreSysAttributes):
for dbus in dbus_loads:
_LOGGER.info("Load dbus interface %s", dbus.name)
try:
await dbus.connect()
await dbus.connect(self._bus)
except Exception as err: # pylint: disable=broad-except
_LOGGER.warning("Can't load dbus interface %s: %s", dbus.name, err)
self.sys_host.supported_features.cache_clear()
async def unload(self) -> None:
"""Close connection to D-Bus."""
self._bus.disconnect()
_LOGGER.info("Closed conection to system D-Bus.")

View File

@ -4,6 +4,7 @@ import logging
from typing import Any
from awesomeversion import AwesomeVersion, AwesomeVersionException
from dbus_next.aio.message_bus import MessageBus
import sentry_sdk
from ...exceptions import (
@ -92,7 +93,7 @@ class NetworkManager(DBusInterface):
)
obj_active_con = result[0]
active_con = NetworkConnection(obj_active_con)
await active_con.connect()
await active_con.connect(self.dbus.bus)
return active_con
@dbus_connected
@ -106,7 +107,9 @@ class NetworkManager(DBusInterface):
con_setting = NetworkSetting(obj_con_setting)
active_con = NetworkConnection(obj_active_con)
await asyncio.gather(con_setting.connect(), active_con.connect())
await asyncio.gather(
con_setting.connect(self.dbus.bus), active_con.connect(self.dbus.bus)
)
return con_setting, active_con
@dbus_connected
@ -117,12 +120,12 @@ class NetworkManager(DBusInterface):
else:
return await self.dbus.get_property(DBUS_IFACE_NM, DBUS_ATTR_CONNECTIVITY)
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_NM, DBUS_OBJECT_NM)
await self.dns.connect()
await self.settings.connect()
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, DBUS_OBJECT_NM)
await self.dns.connect(bus)
await self.settings.connect(bus)
except DBusError:
_LOGGER.warning("Can't connect to Network Manager")
except DBusInterfaceError:
@ -167,7 +170,7 @@ class NetworkManager(DBusInterface):
# Connect to interface
try:
await interface.connect()
await interface.connect(self.dbus.bus)
except (DBusFatalError, DBusInterfaceMethodError) as err:
# Docker creates and deletes interfaces quite often, sometimes
# this causes a race condition: A device disappears while we

View File

@ -1,5 +1,7 @@
"""Connection object for Network Manager."""
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import (
DBUS_ATTR_FREQUENCY,
@ -54,7 +56,7 @@ class NetworkWirelessAP(DBusInterfaceProxy):
"""Return details about mac address."""
return int(self.properties[DBUS_ATTR_STRENGTH])
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_NM, self.object_path)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, self.object_path)
self.properties = await self.dbus.get_properties(DBUS_IFACE_ACCESSPOINT)

View File

@ -1,6 +1,8 @@
"""Connection object for Network Manager."""
from ipaddress import ip_address, ip_interface
from dbus_next.aio.message_bus import MessageBus
from ...const import ATTR_ADDRESS, ATTR_PREFIX
from ...utils.dbus import DBus
from ..const import (
@ -89,9 +91,9 @@ class NetworkConnection(DBusInterfaceProxy):
"""Return a ip6 configuration object for the connection."""
return self._ipv6
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_NM, self.object_path)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, self.object_path)
await self.update()
@dbus_connected
@ -108,7 +110,9 @@ class NetworkConnection(DBusInterfaceProxy):
# IPv4
if self.properties[DBUS_ATTR_IP4CONFIG] != DBUS_OBJECT_BASE:
ip4 = await DBus.connect(DBUS_NAME_NM, self.properties[DBUS_ATTR_IP4CONFIG])
ip4 = await DBus.connect(
self.dbus.bus, DBUS_NAME_NM, self.properties[DBUS_ATTR_IP4CONFIG]
)
ip4_data = await ip4.get_properties(DBUS_IFACE_IP4CONFIG)
self._ipv4 = IpConfiguration(
@ -127,7 +131,9 @@ class NetworkConnection(DBusInterfaceProxy):
# IPv6
if self.properties[DBUS_ATTR_IP6CONFIG] != DBUS_OBJECT_BASE:
ip6 = await DBus.connect(DBUS_NAME_NM, self.properties[DBUS_ATTR_IP6CONFIG])
ip6 = await DBus.connect(
self.dbus.bus, DBUS_NAME_NM, self.properties[DBUS_ATTR_IP6CONFIG]
)
ip6_data = await ip6.get_properties(DBUS_IFACE_IP6CONFIG)
self._ipv6 = IpConfiguration(

View File

@ -2,6 +2,8 @@
from ipaddress import ip_address
import logging
from dbus_next.aio.message_bus import MessageBus
from ...const import (
ATTR_DOMAINS,
ATTR_INTERFACE,
@ -53,10 +55,10 @@ class NetworkManagerDNS(DBusInterface):
"""Return Propertie configuraton."""
return self._configuration
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_NM, DBUS_OBJECT_DNS)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, DBUS_OBJECT_DNS)
except DBusError:
_LOGGER.warning("Can't connect to DnsManager")
except DBusInterfaceError:

View File

@ -1,4 +1,7 @@
"""NetworkInterface object for Network Manager."""
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import (
DBUS_ATTR_ACTIVE_CONNECTION,
@ -74,9 +77,9 @@ class NetworkInterface(DBusInterfaceProxy):
"""Return the wireless data for this interface."""
return self._wireless
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get device information."""
self.dbus = await DBus.connect(DBUS_NAME_NM, self.object_path)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, self.object_path)
self.properties = await self.dbus.get_properties(DBUS_IFACE_DEVICE)
# Abort if device is not managed
@ -88,14 +91,14 @@ class NetworkInterface(DBusInterfaceProxy):
self._connection = NetworkConnection(
self.properties[DBUS_ATTR_ACTIVE_CONNECTION]
)
await self._connection.connect()
await self._connection.connect(bus)
# Attach settings
if self.connection and self.connection.setting_object != DBUS_OBJECT_BASE:
self._settings = NetworkSetting(self.connection.setting_object)
await self._settings.connect()
await self._settings.connect(bus)
# Wireless
if self.type == DeviceType.WIRELESS:
self._wireless = NetworkWireless(self.object_path)
await self._wireless.connect()
await self._wireless.connect(bus)

View File

@ -2,6 +2,8 @@
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ....const import ATTR_METHOD, ATTR_MODE, ATTR_PSK, ATTR_SSID
from ....utils.dbus import DBus
from ...const import DBUS_NAME_NM
@ -158,9 +160,9 @@ class NetworkSetting(DBusInterfaceProxy):
"""Delete connection settings."""
await self.dbus.Settings.Connection.Delete()
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_NM, self.object_path)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, self.object_path)
data = await self.get_settings()
# Get configuration settings we care about

View File

@ -2,12 +2,13 @@
import logging
from typing import Any
from supervisor.dbus.network.setting import NetworkSetting
from dbus_next.aio.message_bus import MessageBus
from ...exceptions import DBusError, DBusInterfaceError
from ...utils.dbus import DBus
from ..const import DBUS_NAME_NM, DBUS_OBJECT_SETTINGS
from ..interface import DBusInterface
from ..network.setting import NetworkSetting
from ..utils import dbus_connected
_LOGGER: logging.Logger = logging.getLogger(__name__)
@ -19,10 +20,10 @@ class NetworkManagerSettings(DBusInterface):
https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Settings.html
"""
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_NM, DBUS_OBJECT_SETTINGS)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, DBUS_OBJECT_SETTINGS)
except DBusError:
_LOGGER.warning("Can't connect to Network Manager Settings")
except DBusInterfaceError:
@ -37,7 +38,7 @@ class NetworkManagerSettings(DBusInterface):
await self.dbus.Settings.AddConnection(("a{sa{sv}}", settings))
)[0]
con_setting = NetworkSetting(obj_con_setting)
await con_setting.connect()
await con_setting.connect(self.dbus.bus)
return con_setting
@dbus_connected

View File

@ -2,6 +2,8 @@
import asyncio
import logging
from dbus_next.aio.message_bus import MessageBus
from ...utils.dbus import DBus
from ..const import (
DBUS_ATTR_ACTIVE_ACCESSPOINT,
@ -46,16 +48,16 @@ class NetworkWireless(DBusInterfaceProxy):
accesspoints = [NetworkWirelessAP(ap_obj) for ap_obj in accesspoints_data]
for err in await asyncio.gather(
*[ap.connect() for ap in accesspoints], return_exceptions=True
*[ap.connect(self.dbus.bus) for ap in accesspoints], return_exceptions=True
):
if err:
_LOGGER.warning("Can't process an AP: %s", err)
return accesspoints
async def connect(self) -> None:
async def connect(self, bus: MessageBus) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_NM, self.object_path)
self.dbus = await DBus.connect(bus, DBUS_NAME_NM, self.object_path)
self.properties = await self.dbus.get_properties(DBUS_IFACE_DEVICE_WIRELESS)
# Get details from current active
@ -63,4 +65,4 @@ class NetworkWireless(DBusInterfaceProxy):
self._active = NetworkWirelessAP(
self.properties[DBUS_ATTR_ACTIVE_ACCESSPOINT]
)
await self._active.connect()
await self._active.connect(bus)

View File

@ -2,6 +2,8 @@
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus, DBusSignalWrapper
from .const import (
@ -35,10 +37,10 @@ class Rauc(DBusInterface):
self._variant: str | None = None
self._boot_slot: str | None = None
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_RAUC, DBUS_OBJECT_BASE)
self.dbus = await DBus.connect(bus, DBUS_NAME_RAUC, DBUS_OBJECT_BASE)
except DBusError:
_LOGGER.warning("Can't connect to rauc")
except DBusInterfaceError:

View File

@ -4,6 +4,8 @@ from __future__ import annotations
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from .const import (
@ -54,10 +56,12 @@ class Resolved(DBusInterface):
"""Initialize Properties."""
self.properties: dict[str, Any] = {}
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_RESOLVED, DBUS_OBJECT_RESOLVED)
self.dbus = await DBus.connect(
bus, DBUS_NAME_RESOLVED, DBUS_OBJECT_RESOLVED
)
except DBusError:
_LOGGER.warning("Can't connect to systemd-resolved.")
except DBusInterfaceError:

View File

@ -2,6 +2,8 @@
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from .const import (
@ -32,10 +34,10 @@ class Systemd(DBusInterface):
"""Initialize Properties."""
self.properties: dict[str, Any] = {}
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_SYSTEMD, DBUS_OBJECT_SYSTEMD)
self.dbus = await DBus.connect(bus, DBUS_NAME_SYSTEMD, DBUS_OBJECT_SYSTEMD)
except DBusError:
_LOGGER.warning("Can't connect to systemd")
except DBusInterfaceError:

View File

@ -3,6 +3,8 @@ from datetime import datetime
import logging
from typing import Any
from dbus_next.aio.message_bus import MessageBus
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from ..utils.dt import utc_from_timestamp
@ -57,10 +59,12 @@ class TimeDate(DBusInterface):
"""Return the system UTC time."""
return utc_from_timestamp(self.properties[DBUS_ATTR_TIMEUSEC] / 1000000)
async def connect(self):
async def connect(self, bus: MessageBus):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_TIMEDATE, DBUS_OBJECT_TIMEDATE)
self.dbus = await DBus.connect(
bus, DBUS_NAME_TIMEDATE, DBUS_OBJECT_TIMEDATE
)
except DBusError:
_LOGGER.warning("Can't connect to systemd-timedate")
except DBusInterfaceError:

View File

@ -5,8 +5,8 @@ import asyncio
import logging
from typing import Any
from dbus_next import BusType, InvalidIntrospectionError, Message, MessageType
from dbus_next.aio import MessageBus
from dbus_next import InvalidIntrospectionError, Message, MessageType
from dbus_next.aio.message_bus import MessageBus
from dbus_next.introspection import Node
from dbus_next.signature import Variant
@ -45,23 +45,18 @@ DBUS_METHOD_SET: str = "org.freedesktop.DBus.Properties.Set"
class DBus:
"""DBus handler."""
def __init__(self, bus_name: str, object_path: str) -> None:
def __init__(self, bus: MessageBus, bus_name: str, object_path: str) -> None:
"""Initialize dbus object."""
self.bus_name: str = bus_name
self.object_path: str = object_path
self.methods: set[str] = set()
self.signals: set[str] = set()
self._bus: MessageBus | None = None
def __del__(self):
"""Delete dbus object."""
if self._bus:
self._bus.disconnect()
self._bus: MessageBus = bus
@staticmethod
async def connect(bus_name: str, object_path: str) -> DBus:
async def connect(bus: MessageBus, bus_name: str, object_path: str) -> DBus:
"""Read object data."""
self = DBus(bus_name, object_path)
self = DBus(bus, bus_name, object_path)
# pylint: disable=protected-access
await self._init_proxy()
@ -69,6 +64,11 @@ class DBus:
_LOGGER.debug("Connect to D-Bus: %s - %s", bus_name, object_path)
return self
@property
def bus(self) -> MessageBus:
"""Return message bus."""
return self._bus
def _add_interfaces(self, introspection: Any):
# Read available methods
for interface in introspection.interfaces:
@ -88,10 +88,6 @@ class DBus:
"""Read interface data."""
# Wait for dbus object to be available after restart
introspection: Node | None = None
try:
self._bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
except Exception as err:
raise DBusFatalError() from err
for _ in range(3):
try:

View File

@ -29,7 +29,7 @@ async def test_llmnr_mdns_info(api_client, coresys: CoreSys):
assert result["data"]["llmnr"] is False
assert result["data"]["mdns"] is False
await coresys.dbus.resolved.connect()
await coresys.dbus.resolved.connect(coresys.dbus.bus)
await coresys.dbus.resolved.update()
resp = await api_client.get("/dns/info")

View File

@ -23,7 +23,7 @@ async def test_api_host_info(api_client, coresys_disk_info: CoreSys):
"""Test host info api."""
coresys = coresys_disk_info
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
resp = await api_client.get("/host/info")
@ -106,7 +106,7 @@ async def test_api_llmnr_mdns_info(api_client, coresys_disk_info: CoreSys):
assert result["data"]["llmnr_hostname"] is None
coresys.host.sys_dbus.resolved.is_connected = True
await coresys.dbus.resolved.connect()
await coresys.dbus.resolved.connect(coresys.dbus.bus)
await coresys.dbus.resolved.update()
resp = await api_client.get("/host/info")

View File

@ -29,7 +29,7 @@ async def test_api_os_info(api_client):
@pytest.mark.asyncio
async def test_api_os_info_with_agent(api_client, coresys: CoreSys):
"""Test docker info api."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
resp = await api_client.get("/os/info")
@ -41,7 +41,7 @@ async def test_api_os_info_with_agent(api_client, coresys: CoreSys):
@pytest.mark.asyncio
async def test_api_os_datadisk_move(api_client, coresys: CoreSys):
"""Test datadisk move without exists disk."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
coresys.os._available = True
@ -54,7 +54,7 @@ async def test_api_os_datadisk_move(api_client, coresys: CoreSys):
@pytest.mark.asyncio
async def test_api_os_datadisk_list(api_client, coresys: CoreSys):
"""Test datadisk list function."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
coresys.hardware.update_device(

View File

@ -10,6 +10,7 @@ from uuid import uuid4
from aiohttp import web
from awesomeversion import AwesomeVersion
from dbus_next import introspection as intr
from dbus_next.aio.message_bus import MessageBus
import pytest
from securetar import SecureTarFile
@ -103,7 +104,13 @@ def docker() -> DockerAPI:
@pytest.fixture
def dbus() -> DBus:
async def dbus_bus() -> MessageBus:
"""Message bus mock."""
yield AsyncMock(spec=MessageBus)
@pytest.fixture
def dbus(dbus_bus: MessageBus) -> DBus:
"""Mock DBUS."""
dbus_commands = []
@ -189,63 +196,67 @@ def dbus() -> DBus:
new=mock_wait_for_signal,
), patch(
"supervisor.utils.dbus.DBus.get_property", new=mock_get_property
), patch(
"supervisor.dbus.manager.MessageBus.connect", return_value=dbus_bus
):
yield dbus_commands
@pytest.fixture
async def network_manager(dbus) -> NetworkManager:
async def network_manager(dbus, dbus_bus: MessageBus) -> NetworkManager:
"""Mock NetworkManager."""
nm_obj = NetworkManager()
nm_obj.dbus = dbus
# Init
await nm_obj.connect()
await nm_obj.connect(dbus_bus)
await nm_obj.update()
yield nm_obj
async def mock_dbus_interface(dbus: DBus, instance: DBusInterface) -> DBusInterface:
async def mock_dbus_interface(
dbus: DBus, dbus_bus: MessageBus, instance: DBusInterface
) -> DBusInterface:
"""Mock dbus for a DBusInterface instance."""
instance.dbus = dbus
await instance.connect()
await instance.connect(dbus_bus)
return instance
@pytest.fixture
async def hostname(dbus: DBus) -> Hostname:
async def hostname(dbus: DBus, dbus_bus: MessageBus) -> Hostname:
"""Mock Hostname."""
yield await mock_dbus_interface(dbus, Hostname())
yield await mock_dbus_interface(dbus, dbus_bus, Hostname())
@pytest.fixture
async def timedate(dbus: DBus) -> TimeDate:
async def timedate(dbus: DBus, dbus_bus: MessageBus) -> TimeDate:
"""Mock Timedate."""
yield await mock_dbus_interface(dbus, TimeDate())
yield await mock_dbus_interface(dbus, dbus_bus, TimeDate())
@pytest.fixture
async def systemd(dbus: DBus) -> Systemd:
async def systemd(dbus: DBus, dbus_bus: MessageBus) -> Systemd:
"""Mock Systemd."""
yield await mock_dbus_interface(dbus, Systemd())
yield await mock_dbus_interface(dbus, dbus_bus, Systemd())
@pytest.fixture
async def os_agent(dbus: DBus) -> OSAgent:
async def os_agent(dbus: DBus, dbus_bus: MessageBus) -> OSAgent:
"""Mock OSAgent."""
yield await mock_dbus_interface(dbus, OSAgent())
yield await mock_dbus_interface(dbus, dbus_bus, OSAgent())
@pytest.fixture
async def resolved(dbus: DBus) -> Resolved:
async def resolved(dbus: DBus, dbus_bus: MessageBus) -> Resolved:
"""Mock REsolved."""
yield await mock_dbus_interface(dbus, Resolved())
yield await mock_dbus_interface(dbus, dbus_bus, Resolved())
@pytest.fixture
async def coresys(
event_loop, docker, network_manager, aiohttp_client, run_dir
event_loop, docker, network_manager, dbus_bus, aiohttp_client, run_dir
) -> CoreSys:
"""Create a CoreSys Mock."""
with patch("supervisor.bootstrap.initialize_system"), patch(
@ -269,6 +280,7 @@ async def coresys(
coresys_obj._machine_id = uuid4()
# Mock host communication
coresys_obj._dbus._bus = dbus_bus
coresys_obj._dbus._network = network_manager
# Mock docker

View File

@ -8,7 +8,7 @@ async def test_dbus_osagent(coresys: CoreSys):
assert coresys.dbus.agent.version is None
assert coresys.dbus.agent.diagnostics is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
assert coresys.dbus.agent.version == "1.1.0"

View File

@ -11,7 +11,7 @@ async def test_dbus_osagent_apparmor(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.agent.apparmor.version is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
assert coresys.dbus.agent.apparmor.version == "2.13.2"
@ -24,7 +24,7 @@ async def test_dbus_osagent_apparmor_load(coresys: CoreSys, dbus: list[str]):
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert (
@ -43,7 +43,7 @@ async def test_dbus_osagent_apparmor_unload(coresys: CoreSys, dbus: list[str]):
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert (

View File

@ -8,11 +8,10 @@ from supervisor.exceptions import DBusNotConnectedError
async def test_dbus_osagent_cgroup_add_devices(coresys: CoreSys, dbus: list[str]):
"""Test wipe data partition on host."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.cgroup.add_devices_allowed("9324kl23j4kl", "*:* rwm")
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert (

View File

@ -11,7 +11,7 @@ async def test_dbus_osagent_datadisk(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.agent.datadisk.current_device is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
assert coresys.dbus.agent.datadisk.current_device.as_posix() == "/dev/sda"
@ -22,7 +22,7 @@ async def test_dbus_osagent_datadisk_change_device(coresys: CoreSys, dbus: list[
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.datadisk.change_device(Path("/dev/sdb"))
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.agent.datadisk.change_device(Path("/dev/sdb")) is None
@ -34,7 +34,7 @@ async def test_dbus_osagent_datadisk_reload_device(coresys: CoreSys, dbus: list[
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.datadisk.reload_device()
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.agent.datadisk.reload_device() is None

View File

@ -11,7 +11,7 @@ async def test_dbus_osagent_system_wipe(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.system.schedule_wipe_device()
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.agent.system.schedule_wipe_device() is None

View File

@ -134,7 +134,7 @@ async def mock_call_dbus_get_settings_signature(
async def test_update(coresys: CoreSys):
"""Test network manager update."""
await coresys.dbus.network.interfaces[TEST_INTERFACE].connect()
await coresys.dbus.network.interfaces[TEST_INTERFACE].connect(coresys.dbus.bus)
interface = Interface.from_dbus_interface(
coresys.dbus.network.interfaces[TEST_INTERFACE]
)
@ -154,7 +154,7 @@ async def test_update(coresys: CoreSys):
async def test_ipv6_disabled_is_link_local(coresys: CoreSys):
"""Test disabled equals link local for ipv6."""
await coresys.dbus.network.interfaces[TEST_INTERFACE].connect()
await coresys.dbus.network.interfaces[TEST_INTERFACE].connect(coresys.dbus.bus)
interface = Interface.from_dbus_interface(
coresys.dbus.network.interfaces[TEST_INTERFACE]
)

View File

@ -1,15 +1,17 @@
"""Test NetworkWireless AP object."""
from dbus_next.aio.message_bus import MessageBus
from supervisor.dbus.network.accesspoint import NetworkWirelessAP
async def test_accesspoint(dbus: list[str]):
async def test_accesspoint(dbus: list[str], dbus_bus: MessageBus):
"""Test accesspoint."""
wireless_ap = NetworkWirelessAP("/org/freedesktop/NetworkManager/AccessPoint/43099")
assert wireless_ap.mac is None
assert wireless_ap.mode is None
await wireless_ap.connect()
await wireless_ap.connect(dbus_bus)
assert wireless_ap.mac == "E4:57:40:A9:D7:DE"
assert wireless_ap.mode == 2

View File

@ -10,7 +10,7 @@ async def test_dbus_hostname_info(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.hostname.hostname is None
await coresys.dbus.hostname.connect()
await coresys.dbus.hostname.connect(coresys.dbus.bus)
await coresys.dbus.hostname.update()
assert coresys.dbus.hostname.hostname == "homeassistant-n2"
@ -27,7 +27,7 @@ async def test_dbus_sethostname(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.hostname.set_static_hostname("StarWars")
await coresys.dbus.hostname.connect()
await coresys.dbus.hostname.connect(coresys.dbus.bus)
dbus.clear()
await coresys.dbus.hostname.set_static_hostname("StarWars")

View File

@ -10,7 +10,7 @@ async def test_reboot(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.logind.reboot()
await coresys.dbus.logind.connect()
await coresys.dbus.logind.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.logind.reboot() is None
@ -22,7 +22,7 @@ async def test_power_off(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.logind.power_off()
await coresys.dbus.logind.connect()
await coresys.dbus.logind.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.logind.power_off() is None

View File

@ -11,7 +11,7 @@ async def test_rauc(coresys: CoreSys):
assert coresys.dbus.rauc.boot_slot is None
assert coresys.dbus.rauc.operation is None
await coresys.dbus.rauc.connect()
await coresys.dbus.rauc.connect(coresys.dbus.bus)
await coresys.dbus.rauc.update()
assert coresys.dbus.rauc.boot_slot == "B"
@ -23,7 +23,7 @@ async def test_install(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.rauc.install("rauc_file")
await coresys.dbus.rauc.connect()
await coresys.dbus.rauc.connect(coresys.dbus.bus)
dbus.clear()
async with coresys.dbus.rauc.signal_completed() as signal:
@ -38,7 +38,7 @@ async def test_get_slot_status(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.rauc.get_slot_status()
await coresys.dbus.rauc.connect()
await coresys.dbus.rauc.connect(coresys.dbus.bus)
dbus.clear()
slot_status = await coresys.dbus.rauc.get_slot_status()
@ -59,7 +59,7 @@ async def test_mark(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.rauc.mark(RaucState.GOOD, "booted")
await coresys.dbus.rauc.connect()
await coresys.dbus.rauc.connect(coresys.dbus.bus)
dbus.clear()
mark = await coresys.dbus.rauc.mark(RaucState.GOOD, "booted")

View File

@ -52,7 +52,7 @@ async def test_dbus_resolved_info(coresys_ip_bytes: CoreSys):
assert coresys.dbus.resolved.dns is None
await coresys.dbus.resolved.connect()
await coresys.dbus.resolved.connect(coresys.dbus.bus)
await coresys.dbus.resolved.update()
assert coresys.dbus.resolved.llmnr_hostname == "homeassistant"

View File

@ -16,7 +16,7 @@ async def test_dbus_systemd_info(coresys: CoreSys):
assert coresys.dbus.systemd.boot_timestamp is None
assert coresys.dbus.systemd.startup_time is None
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
async def mock_get_properties(dbus_obj, interface):
return load_json_fixture(
@ -35,7 +35,7 @@ async def test_reboot(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.reboot()
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.systemd.reboot() is None
@ -47,7 +47,7 @@ async def test_power_off(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.power_off()
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.systemd.power_off() is None
@ -61,7 +61,7 @@ async def test_start_unit(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.start_unit("test_unit", "replace")
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert (
@ -78,7 +78,7 @@ async def test_stop_unit(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.stop_unit("test_unit", "replace")
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert (
@ -95,7 +95,7 @@ async def test_restart_unit(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.restart_unit("test_unit", "replace")
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert (
@ -112,7 +112,7 @@ async def test_reload_unit(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.reload_unit("test_unit", "replace")
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
assert (
@ -129,7 +129,7 @@ async def test_list_units(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.systemd.list_units()
await coresys.dbus.systemd.connect()
await coresys.dbus.systemd.connect(coresys.dbus.bus)
dbus.clear()
units = await coresys.dbus.systemd.list_units()

View File

@ -11,7 +11,7 @@ async def test_dbus_timezone(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.timedate.dt_utc is None
await coresys.dbus.timedate.connect()
await coresys.dbus.timedate.connect(coresys.dbus.bus)
await coresys.dbus.timedate.update()
assert coresys.dbus.timedate.dt_utc == datetime(
@ -30,7 +30,7 @@ async def test_dbus_settime(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.timedate.set_time(test_dt)
await coresys.dbus.timedate.connect()
await coresys.dbus.timedate.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.timedate.set_time(test_dt) is None
@ -42,7 +42,7 @@ async def test_dbus_setntp(coresys: CoreSys, dbus: list[str]):
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.timedate.set_ntp(False)
await coresys.dbus.timedate.connect()
await coresys.dbus.timedate.connect(coresys.dbus.bus)
dbus.clear()
assert await coresys.dbus.timedate.set_ntp(False) is None

View File

@ -13,7 +13,7 @@ from supervisor.hardware.data import Device
@pytest.mark.asyncio
async def tests_datadisk_current(coresys: CoreSys):
"""Test current datadisk."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
assert coresys.os.datadisk.disk_used == PosixPath("/dev/sda")
@ -22,7 +22,7 @@ async def tests_datadisk_current(coresys: CoreSys):
@pytest.mark.asyncio
async def test_datadisk_move(coresys: CoreSys):
"""Test datadisk moved without exists device."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
coresys.os._available = True
@ -33,7 +33,7 @@ async def test_datadisk_move(coresys: CoreSys):
@pytest.mark.asyncio
async def test_datadisk_list(coresys: CoreSys):
"""Test docker info api."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.connect(coresys.dbus.bus)
await coresys.dbus.agent.update()
coresys.hardware.update_device(

View File

@ -14,7 +14,7 @@ async def test_timezone(run_dir, coresys: CoreSys):
assert coresys.timezone == "UTC"
assert coresys.config.timezone is None
await coresys.dbus.timedate.connect()
await coresys.dbus.timedate.connect(coresys.dbus.bus)
await coresys.dbus.timedate.update()
assert coresys.timezone == "Etc/UTC"

View File

@ -22,8 +22,11 @@ def test_remove_dbus_signature():
async def test_dbus_prepare_args(coresys: CoreSys):
"""Check D-Bus dynamic argument builder."""
dbus = DBus("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
signature, args = dbus._prepare_args(
dbus = DBus(
coresys.dbus.bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1"
)
# pylint: disable=protected-access
signature, _ = dbus._prepare_args(
True, 1, 1.0, "Value", ("a{sv}", {"Key": "Value"})
)
assert signature == "bidsa{sv}"