mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 21:56:29 +00:00
Refactor to dbus-next proxy interfaces (#3862)
* Refactor to dbus-next proxy interfaces * Fix tests mocking dbus methods * Fix call dbus
This commit is contained in:
parent
c67d4d7c0b
commit
d195f19fa8
@ -75,9 +75,7 @@ class OSAgent(DBusInterface):
|
|||||||
@dbus_property
|
@dbus_property
|
||||||
def diagnostics(self, value: bool) -> None:
|
def diagnostics(self, value: bool) -> None:
|
||||||
"""Enable or disable OS-Agent diagnostics."""
|
"""Enable or disable OS-Agent diagnostics."""
|
||||||
asyncio.create_task(
|
asyncio.create_task(self.dbus.set_diagnostics(value))
|
||||||
self.dbus.set_property(DBUS_IFACE_HAOS, DBUS_ATTR_DIAGNOSTICS, value)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def connect(self, bus: MessageBus) -> None:
|
async def connect(self, bus: MessageBus) -> None:
|
||||||
"""Connect to system's D-Bus."""
|
"""Connect to system's D-Bus."""
|
||||||
|
@ -41,9 +41,11 @@ class AppArmor(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def load_profile(self, profile: Path, cache: Path) -> None:
|
async def load_profile(self, profile: Path, cache: Path) -> None:
|
||||||
"""Load/Update AppArmor profile."""
|
"""Load/Update AppArmor profile."""
|
||||||
await self.dbus.AppArmor.LoadProfile(profile.as_posix(), cache.as_posix())
|
await self.dbus.AppArmor.call_load_profile(profile.as_posix(), cache.as_posix())
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def unload_profile(self, profile: Path, cache: Path) -> None:
|
async def unload_profile(self, profile: Path, cache: Path) -> None:
|
||||||
"""Remove AppArmor profile."""
|
"""Remove AppArmor profile."""
|
||||||
await self.dbus.AppArmor.UnloadProfile(profile.as_posix(), cache.as_posix())
|
await self.dbus.AppArmor.call_unload_profile(
|
||||||
|
profile.as_posix(), cache.as_posix()
|
||||||
|
)
|
||||||
|
@ -18,4 +18,4 @@ class CGroup(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def add_devices_allowed(self, container_id: str, permission: str) -> None:
|
async def add_devices_allowed(self, container_id: str, permission: str) -> None:
|
||||||
"""Update cgroup devices and add new devices."""
|
"""Update cgroup devices and add new devices."""
|
||||||
await self.dbus.CGroup.AddDevicesAllowed(container_id, permission)
|
await self.dbus.CGroup.call_add_devices_allowed(container_id, permission)
|
||||||
|
@ -40,9 +40,9 @@ class DataDisk(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def change_device(self, device: Path) -> None:
|
async def change_device(self, device: Path) -> None:
|
||||||
"""Migrate data disk to a new device."""
|
"""Migrate data disk to a new device."""
|
||||||
await self.dbus.DataDisk.ChangeDevice(device.as_posix())
|
await self.dbus.DataDisk.call_change_device(device.as_posix())
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def reload_device(self) -> None:
|
async def reload_device(self) -> None:
|
||||||
"""Reload device data."""
|
"""Reload device data."""
|
||||||
await self.dbus.DataDisk.ReloadDevice()
|
await self.dbus.DataDisk.call_reload_device()
|
||||||
|
@ -18,4 +18,4 @@ class System(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def schedule_wipe_device(self) -> None:
|
async def schedule_wipe_device(self) -> None:
|
||||||
"""Schedule a factory reset on next system boot."""
|
"""Schedule a factory reset on next system boot."""
|
||||||
await self.dbus.System.ScheduleWipeDevice()
|
await self.dbus.System.call_schedule_wipe_device()
|
||||||
|
@ -87,7 +87,7 @@ class Hostname(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def set_static_hostname(self, hostname: str) -> None:
|
async def set_static_hostname(self, hostname: str) -> None:
|
||||||
"""Change local hostname."""
|
"""Change local hostname."""
|
||||||
await self.dbus.SetStaticHostname(hostname, False)
|
await self.dbus.call_set_static_hostname(hostname, False)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def update(self):
|
async def update(self):
|
||||||
|
@ -32,9 +32,9 @@ class Logind(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def reboot(self) -> None:
|
async def reboot(self) -> None:
|
||||||
"""Reboot host computer."""
|
"""Reboot host computer."""
|
||||||
await self.dbus.Manager.Reboot(False)
|
await self.dbus.Manager.call_reboot(False)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def power_off(self) -> None:
|
async def power_off(self) -> None:
|
||||||
"""Power off host computer."""
|
"""Power off host computer."""
|
||||||
await self.dbus.Manager.PowerOff(False)
|
await self.dbus.Manager.call_power_off(False)
|
||||||
|
@ -112,7 +112,7 @@ class DBusManager(CoreSysAttributes):
|
|||||||
for dbus in dbus_loads:
|
for dbus in dbus_loads:
|
||||||
_LOGGER.info("Load dbus interface %s", dbus.name)
|
_LOGGER.info("Load dbus interface %s", dbus.name)
|
||||||
try:
|
try:
|
||||||
await dbus.connect(self._bus)
|
await dbus.connect(self.bus)
|
||||||
except Exception as err: # pylint: disable=broad-except
|
except Exception as err: # pylint: disable=broad-except
|
||||||
_LOGGER.warning("Can't load dbus interface %s: %s", dbus.name, err)
|
_LOGGER.warning("Can't load dbus interface %s: %s", dbus.name, err)
|
||||||
|
|
||||||
@ -120,5 +120,9 @@ class DBusManager(CoreSysAttributes):
|
|||||||
|
|
||||||
async def unload(self) -> None:
|
async def unload(self) -> None:
|
||||||
"""Close connection to D-Bus."""
|
"""Close connection to D-Bus."""
|
||||||
self._bus.disconnect()
|
if not self.bus:
|
||||||
|
_LOGGER.warning("No D-Bus connection to close.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.bus.disconnect()
|
||||||
_LOGGER.info("Closed conection to system D-Bus.")
|
_LOGGER.info("Closed conection to system D-Bus.")
|
||||||
|
@ -17,7 +17,6 @@ from ...exceptions import (
|
|||||||
from ...utils.dbus import DBus
|
from ...utils.dbus import DBus
|
||||||
from ..const import (
|
from ..const import (
|
||||||
DBUS_ATTR_CONNECTION_ENABLED,
|
DBUS_ATTR_CONNECTION_ENABLED,
|
||||||
DBUS_ATTR_CONNECTIVITY,
|
|
||||||
DBUS_ATTR_DEVICES,
|
DBUS_ATTR_DEVICES,
|
||||||
DBUS_ATTR_PRIMARY_CONNECTION,
|
DBUS_ATTR_PRIMARY_CONNECTION,
|
||||||
DBUS_ATTR_VERSION,
|
DBUS_ATTR_VERSION,
|
||||||
@ -88,10 +87,9 @@ class NetworkManager(DBusInterface):
|
|||||||
self, connection_object: str, device_object: str
|
self, connection_object: str, device_object: str
|
||||||
) -> NetworkConnection:
|
) -> NetworkConnection:
|
||||||
"""Activate a connction on a device."""
|
"""Activate a connction on a device."""
|
||||||
result = await self.dbus.ActivateConnection(
|
obj_active_con = await self.dbus.call_activate_connection(
|
||||||
("o", connection_object), ("o", device_object), ("o", DBUS_OBJECT_BASE)
|
connection_object, device_object, DBUS_OBJECT_BASE
|
||||||
)
|
)
|
||||||
obj_active_con = result[0]
|
|
||||||
active_con = NetworkConnection(obj_active_con)
|
active_con = NetworkConnection(obj_active_con)
|
||||||
await active_con.connect(self.dbus.bus)
|
await active_con.connect(self.dbus.bus)
|
||||||
return active_con
|
return active_con
|
||||||
@ -101,8 +99,11 @@ class NetworkManager(DBusInterface):
|
|||||||
self, settings: Any, device_object: str
|
self, settings: Any, device_object: str
|
||||||
) -> tuple[NetworkSetting, NetworkConnection]:
|
) -> tuple[NetworkSetting, NetworkConnection]:
|
||||||
"""Activate a connction on a device."""
|
"""Activate a connction on a device."""
|
||||||
obj_con_setting, obj_active_con = await self.dbus.AddAndActivateConnection(
|
(
|
||||||
("a{sa{sv}}", settings), ("o", device_object), ("o", DBUS_OBJECT_BASE)
|
obj_con_setting,
|
||||||
|
obj_active_con,
|
||||||
|
) = await self.dbus.call_add_and_activate_connection(
|
||||||
|
settings, device_object, DBUS_OBJECT_BASE
|
||||||
)
|
)
|
||||||
|
|
||||||
con_setting = NetworkSetting(obj_con_setting)
|
con_setting = NetworkSetting(obj_con_setting)
|
||||||
@ -116,9 +117,9 @@ class NetworkManager(DBusInterface):
|
|||||||
async def check_connectivity(self, *, force: bool = False) -> int:
|
async def check_connectivity(self, *, force: bool = False) -> int:
|
||||||
"""Check the connectivity of the host."""
|
"""Check the connectivity of the host."""
|
||||||
if force:
|
if force:
|
||||||
return (await self.dbus.CheckConnectivity())[0]
|
return await self.dbus.call_check_connectivity()
|
||||||
else:
|
else:
|
||||||
return await self.dbus.get_property(DBUS_IFACE_NM, DBUS_ATTR_CONNECTIVITY)
|
return await self.dbus.get_connectivity()
|
||||||
|
|
||||||
async def connect(self, bus: MessageBus) -> None:
|
async def connect(self, bus: MessageBus) -> None:
|
||||||
"""Connect to system's D-Bus."""
|
"""Connect to system's D-Bus."""
|
||||||
|
@ -124,14 +124,14 @@ class NetworkSetting(DBusInterfaceProxy):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def get_settings(self) -> dict[str, Any]:
|
async def get_settings(self) -> dict[str, Any]:
|
||||||
"""Return connection settings."""
|
"""Return connection settings."""
|
||||||
return (await self.dbus.Settings.Connection.GetSettings())[0]
|
return await self.dbus.Settings.Connection.call_get_settings()
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def update(self, settings: Any) -> None:
|
async def update(self, settings: Any) -> None:
|
||||||
"""Update connection settings."""
|
"""Update connection settings."""
|
||||||
new_settings = (
|
new_settings = await self.dbus.Settings.Connection.call_get_settings(
|
||||||
await self.dbus.Settings.Connection.GetSettings(remove_signature=False)
|
remove_signature=False
|
||||||
)[0]
|
)
|
||||||
|
|
||||||
_merge_settings_attribute(new_settings, settings, CONF_ATTR_CONNECTION)
|
_merge_settings_attribute(new_settings, settings, CONF_ATTR_CONNECTION)
|
||||||
_merge_settings_attribute(new_settings, settings, CONF_ATTR_802_ETHERNET)
|
_merge_settings_attribute(new_settings, settings, CONF_ATTR_802_ETHERNET)
|
||||||
@ -153,12 +153,12 @@ class NetworkSetting(DBusInterfaceProxy):
|
|||||||
ignore_current_value=IPV4_6_IGNORE_FIELDS,
|
ignore_current_value=IPV4_6_IGNORE_FIELDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self.dbus.Settings.Connection.Update(("a{sa{sv}}", new_settings))
|
await self.dbus.Settings.Connection.call_update(new_settings)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def delete(self) -> None:
|
async def delete(self) -> None:
|
||||||
"""Delete connection settings."""
|
"""Delete connection settings."""
|
||||||
await self.dbus.Settings.Connection.Delete()
|
await self.dbus.Settings.Connection.call_delete()
|
||||||
|
|
||||||
async def connect(self, bus: MessageBus) -> None:
|
async def connect(self, bus: MessageBus) -> None:
|
||||||
"""Get connection information."""
|
"""Get connection information."""
|
||||||
|
@ -34,9 +34,7 @@ class NetworkManagerSettings(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def add_connection(self, settings: Any) -> NetworkSetting:
|
async def add_connection(self, settings: Any) -> NetworkSetting:
|
||||||
"""Add new connection."""
|
"""Add new connection."""
|
||||||
obj_con_setting = (
|
obj_con_setting = await self.dbus.Settings.call_add_connection(settings)
|
||||||
await self.dbus.Settings.AddConnection(("a{sa{sv}}", settings))
|
|
||||||
)[0]
|
|
||||||
con_setting = NetworkSetting(obj_con_setting)
|
con_setting = NetworkSetting(obj_con_setting)
|
||||||
await con_setting.connect(self.dbus.bus)
|
await con_setting.connect(self.dbus.bus)
|
||||||
return con_setting
|
return con_setting
|
||||||
@ -44,4 +42,4 @@ class NetworkManagerSettings(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def reload_connections(self) -> bool:
|
async def reload_connections(self) -> bool:
|
||||||
"""Reload all local connection files."""
|
"""Reload all local connection files."""
|
||||||
return (await self.dbus.Settings.ReloadConnections())[0]
|
return await self.dbus.Settings.call_reload_connections()
|
||||||
|
@ -39,12 +39,12 @@ class NetworkWireless(DBusInterfaceProxy):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def request_scan(self) -> None:
|
async def request_scan(self) -> None:
|
||||||
"""Request a new AP scan."""
|
"""Request a new AP scan."""
|
||||||
await self.dbus.Device.Wireless.RequestScan(("a{sv}", {}))
|
await self.dbus.Device.Wireless.call_request_scan({})
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def get_all_accesspoints(self) -> list[NetworkWirelessAP]:
|
async def get_all_accesspoints(self) -> list[NetworkWirelessAP]:
|
||||||
"""Return a list of all access points path."""
|
"""Return a list of all access points path."""
|
||||||
accesspoints_data = (await self.dbus.Device.Wireless.GetAllAccessPoints())[0]
|
accesspoints_data = await self.dbus.Device.Wireless.call_get_all_access_points()
|
||||||
accesspoints = [NetworkWirelessAP(ap_obj) for ap_obj in accesspoints_data]
|
accesspoints = [NetworkWirelessAP(ap_obj) for ap_obj in accesspoints_data]
|
||||||
|
|
||||||
for err in await asyncio.gather(
|
for err in await asyncio.gather(
|
||||||
|
@ -74,12 +74,12 @@ class Rauc(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def install(self, raucb_file) -> None:
|
async def install(self, raucb_file) -> None:
|
||||||
"""Install rauc bundle file."""
|
"""Install rauc bundle file."""
|
||||||
await self.dbus.Installer.Install(str(raucb_file))
|
await self.dbus.Installer.call_install(str(raucb_file))
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def get_slot_status(self) -> list[tuple[str, dict[str, Any]]]:
|
async def get_slot_status(self) -> list[tuple[str, dict[str, Any]]]:
|
||||||
"""Get slot status."""
|
"""Get slot status."""
|
||||||
return (await self.dbus.Installer.GetSlotStatus())[0]
|
return await self.dbus.Installer.call_get_slot_status()
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
def signal_completed(self) -> DBusSignalWrapper:
|
def signal_completed(self) -> DBusSignalWrapper:
|
||||||
@ -89,7 +89,7 @@ class Rauc(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def mark(self, state: RaucState, slot_identifier: str) -> tuple[str, str]:
|
async def mark(self, state: RaucState, slot_identifier: str) -> tuple[str, str]:
|
||||||
"""Get slot status."""
|
"""Get slot status."""
|
||||||
return await self.dbus.Installer.Mark(state, slot_identifier)
|
return await self.dbus.Installer.call_mark(state, slot_identifier)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def update(self):
|
async def update(self):
|
||||||
|
@ -65,39 +65,39 @@ class Systemd(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def reboot(self) -> None:
|
async def reboot(self) -> None:
|
||||||
"""Reboot host computer."""
|
"""Reboot host computer."""
|
||||||
await self.dbus.Manager.Reboot()
|
await self.dbus.Manager.call_reboot()
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def power_off(self) -> None:
|
async def power_off(self) -> None:
|
||||||
"""Power off host computer."""
|
"""Power off host computer."""
|
||||||
await self.dbus.Manager.PowerOff()
|
await self.dbus.Manager.call_power_off()
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def start_unit(self, unit, mode) -> str:
|
async def start_unit(self, unit, mode) -> str:
|
||||||
"""Start a systemd service unit. Return job object path."""
|
"""Start a systemd service unit. Returns object path of job."""
|
||||||
return (await self.dbus.Manager.StartUnit(unit, mode))[0]
|
return await self.dbus.Manager.call_start_unit(unit, mode)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def stop_unit(self, unit, mode) -> str:
|
async def stop_unit(self, unit, mode) -> str:
|
||||||
"""Stop a systemd service unit."""
|
"""Stop a systemd service unit. Returns object path of job."""
|
||||||
return (await self.dbus.Manager.StopUnit(unit, mode))[0]
|
return await self.dbus.Manager.call_stop_unit(unit, mode)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def reload_unit(self, unit, mode) -> str:
|
async def reload_unit(self, unit, mode) -> str:
|
||||||
"""Reload a systemd service unit."""
|
"""Reload a systemd service unit. Returns object path of job."""
|
||||||
return (await self.dbus.Manager.ReloadOrRestartUnit(unit, mode))[0]
|
return await self.dbus.Manager.call_reload_or_restart_unit(unit, mode)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def restart_unit(self, unit, mode):
|
async def restart_unit(self, unit, mode) -> str:
|
||||||
"""Restart a systemd service unit."""
|
"""Restart a systemd service unit. Returns object path of job."""
|
||||||
return (await self.dbus.Manager.RestartUnit(unit, mode))[0]
|
return await self.dbus.Manager.call_restart_unit(unit, mode)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def list_units(
|
async def list_units(
|
||||||
self,
|
self,
|
||||||
) -> list[str, str, str, str, str, str, str, int, str, str]:
|
) -> list[tuple[str, str, str, str, str, str, str, int, str, str]]:
|
||||||
"""Return a list of available systemd services."""
|
"""Return a list of available systemd services."""
|
||||||
return (await self.dbus.Manager.ListUnits())[0]
|
return await self.dbus.Manager.call_list_units()
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def update(self):
|
async def update(self):
|
||||||
|
@ -75,12 +75,12 @@ class TimeDate(DBusInterface):
|
|||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def set_time(self, utc: datetime) -> None:
|
async def set_time(self, utc: datetime) -> None:
|
||||||
"""Set time & date on host as UTC."""
|
"""Set time & date on host as UTC."""
|
||||||
await self.dbus.SetTime(int(utc.timestamp() * 1000000), False, False)
|
await self.dbus.call_set_time(int(utc.timestamp() * 1000000), False, False)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def set_ntp(self, use_ntp: bool) -> None:
|
async def set_ntp(self, use_ntp: bool) -> None:
|
||||||
"""Turn NTP on or off."""
|
"""Turn NTP on or off."""
|
||||||
await self.dbus.SetNTP(use_ntp, False)
|
await self.dbus.call_set_ntp(use_ntp, False)
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def update(self):
|
async def update(self):
|
||||||
|
@ -315,18 +315,34 @@ class DBusInterfaceError(HassioNotSupportedError):
|
|||||||
"""D-Bus interface not connected."""
|
"""D-Bus interface not connected."""
|
||||||
|
|
||||||
|
|
||||||
|
class DBusObjectError(HassioNotSupportedError):
|
||||||
|
"""D-Bus object not defined."""
|
||||||
|
|
||||||
|
|
||||||
|
class DBusInterfaceMethodError(DBusInterfaceError):
|
||||||
|
"""D-Bus method not defined or input does not match signature."""
|
||||||
|
|
||||||
|
|
||||||
|
class DBusInterfacePropertyError(DBusInterfaceError):
|
||||||
|
"""D-Bus property not defined or is read-only."""
|
||||||
|
|
||||||
|
|
||||||
|
class DBusInterfaceSignalError(DBusInterfaceError):
|
||||||
|
"""D-Bus signal not defined."""
|
||||||
|
|
||||||
|
|
||||||
class DBusFatalError(DBusError):
|
class DBusFatalError(DBusError):
|
||||||
"""D-Bus call going wrong."""
|
"""D-Bus call going wrong."""
|
||||||
|
|
||||||
|
|
||||||
class DBusInterfaceMethodError(DBusInterfaceError):
|
|
||||||
"""D-Bus method was not defined."""
|
|
||||||
|
|
||||||
|
|
||||||
class DBusParseError(DBusError):
|
class DBusParseError(DBusError):
|
||||||
"""D-Bus parse error."""
|
"""D-Bus parse error."""
|
||||||
|
|
||||||
|
|
||||||
|
class DBusTimeoutError(DBusError):
|
||||||
|
"""D-Bus call timed out."""
|
||||||
|
|
||||||
|
|
||||||
# util/apparmor
|
# util/apparmor
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Awaitable, Callable
|
||||||
|
|
||||||
from dbus_next import InvalidIntrospectionError, Message, MessageType
|
from dbus_next import ErrorType, InvalidIntrospectionError, Message, MessageType
|
||||||
from dbus_next.aio.message_bus import MessageBus
|
from dbus_next.aio.message_bus import MessageBus
|
||||||
|
from dbus_next.aio.proxy_object import ProxyInterface, ProxyObject
|
||||||
|
from dbus_next.errors import DBusError
|
||||||
from dbus_next.introspection import Node
|
from dbus_next.introspection import Node
|
||||||
from dbus_next.signature import Variant
|
from dbus_next.signature import Variant
|
||||||
|
|
||||||
@ -14,32 +16,19 @@ from ..exceptions import (
|
|||||||
DBusFatalError,
|
DBusFatalError,
|
||||||
DBusInterfaceError,
|
DBusInterfaceError,
|
||||||
DBusInterfaceMethodError,
|
DBusInterfaceMethodError,
|
||||||
|
DBusInterfacePropertyError,
|
||||||
|
DBusInterfaceSignalError,
|
||||||
DBusNotConnectedError,
|
DBusNotConnectedError,
|
||||||
|
DBusObjectError,
|
||||||
DBusParseError,
|
DBusParseError,
|
||||||
|
DBusTimeoutError,
|
||||||
|
HassioNotSupportedError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _remove_dbus_signature(data: Any) -> Any:
|
|
||||||
if isinstance(data, Variant):
|
|
||||||
return _remove_dbus_signature(data.value)
|
|
||||||
elif isinstance(data, dict):
|
|
||||||
for k in data:
|
|
||||||
data[k] = _remove_dbus_signature(data[k])
|
|
||||||
return data
|
|
||||||
elif isinstance(data, list):
|
|
||||||
new_list = []
|
|
||||||
for item in data:
|
|
||||||
new_list.append(_remove_dbus_signature(item))
|
|
||||||
return new_list
|
|
||||||
else:
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DBUS_INTERFACE_PROPERTIES: str = "org.freedesktop.DBus.Properties"
|
||||||
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
|
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
|
||||||
DBUS_METHOD_GET: str = "org.freedesktop.DBus.Properties.Get"
|
|
||||||
DBUS_METHOD_SET: str = "org.freedesktop.DBus.Properties.Set"
|
|
||||||
|
|
||||||
|
|
||||||
class DBus:
|
class DBus:
|
||||||
@ -49,8 +38,8 @@ class DBus:
|
|||||||
"""Initialize dbus object."""
|
"""Initialize dbus object."""
|
||||||
self.bus_name: str = bus_name
|
self.bus_name: str = bus_name
|
||||||
self.object_path: str = object_path
|
self.object_path: str = object_path
|
||||||
self.methods: set[str] = set()
|
self._proxy_obj: ProxyObject | None = None
|
||||||
self.signals: set[str] = set()
|
self._proxies: dict[str, ProxyInterface] = {}
|
||||||
self._bus: MessageBus = bus
|
self._bus: MessageBus = bus
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -64,29 +53,73 @@ class DBus:
|
|||||||
_LOGGER.debug("Connect to D-Bus: %s - %s", bus_name, object_path)
|
_LOGGER.debug("Connect to D-Bus: %s - %s", bus_name, object_path)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@property
|
@staticmethod
|
||||||
def bus(self) -> MessageBus:
|
def remove_dbus_signature(data: Any) -> Any:
|
||||||
"""Return message bus."""
|
"""Remove signature info."""
|
||||||
return self._bus
|
if isinstance(data, Variant):
|
||||||
|
return DBus.remove_dbus_signature(data.value)
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
for k in data:
|
||||||
|
data[k] = DBus.remove_dbus_signature(data[k])
|
||||||
|
return data
|
||||||
|
elif isinstance(data, list):
|
||||||
|
new_list = []
|
||||||
|
for item in data:
|
||||||
|
new_list.append(DBus.remove_dbus_signature(item))
|
||||||
|
return new_list
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
def _add_interfaces(self, introspection: Any):
|
@staticmethod
|
||||||
# Read available methods
|
def from_dbus_error(err: DBusError) -> HassioNotSupportedError | DBusError:
|
||||||
for interface in introspection.interfaces:
|
"""Return correct dbus error based on type."""
|
||||||
interface_name = interface.name
|
if err.type in {ErrorType.SERVICE_UNKNOWN, ErrorType.UNKNOWN_INTERFACE}:
|
||||||
|
return DBusInterfaceError(err.text)
|
||||||
|
if err.type in {
|
||||||
|
ErrorType.UNKNOWN_METHOD,
|
||||||
|
ErrorType.INVALID_SIGNATURE,
|
||||||
|
ErrorType.INVALID_ARGS,
|
||||||
|
}:
|
||||||
|
return DBusInterfaceMethodError(err.text)
|
||||||
|
if err.type == ErrorType.UNKNOWN_OBJECT:
|
||||||
|
return DBusObjectError(err.text)
|
||||||
|
if err.type in {ErrorType.UNKNOWN_PROPERTY, ErrorType.PROPERTY_READ_ONLY}:
|
||||||
|
return DBusInterfacePropertyError(err.text)
|
||||||
|
if err.type == ErrorType.DISCONNECTED:
|
||||||
|
return DBusNotConnectedError(err.text)
|
||||||
|
if err.type == ErrorType.TIMEOUT:
|
||||||
|
return DBusTimeoutError(err.text)
|
||||||
|
return DBusFatalError(err.text)
|
||||||
|
|
||||||
# Methods
|
@staticmethod
|
||||||
for method in interface.methods:
|
async def call_dbus(
|
||||||
method_name = method.name
|
proxy_interface: ProxyInterface,
|
||||||
self.methods.add(f"{interface_name}.{method_name}")
|
method: str,
|
||||||
|
*args,
|
||||||
|
remove_signature: bool = True,
|
||||||
|
) -> Any:
|
||||||
|
"""Call a dbus method and handle the signature and errors."""
|
||||||
|
_LOGGER.debug(
|
||||||
|
"D-Bus call - %s.%s on %s",
|
||||||
|
proxy_interface.introspection.name,
|
||||||
|
method,
|
||||||
|
proxy_interface.path,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
body = await getattr(proxy_interface, method)(*args)
|
||||||
|
return DBus.remove_dbus_signature(body) if remove_signature else body
|
||||||
|
except DBusError as err:
|
||||||
|
raise DBus.from_dbus_error(err)
|
||||||
|
|
||||||
# Signals
|
def _add_interfaces(self):
|
||||||
for signal in interface.signals:
|
"""Make proxy interfaces out of introspection data."""
|
||||||
signal_name = signal.name
|
self._proxies = {
|
||||||
self.signals.add(f"{interface_name}.{signal_name}")
|
interface.name: self._proxy_obj.get_interface(interface.name)
|
||||||
|
for interface in self._proxy_obj.introspection.interfaces
|
||||||
|
}
|
||||||
|
|
||||||
async def _init_proxy(self) -> None:
|
async def _init_proxy(self) -> None:
|
||||||
"""Read interface data."""
|
"""Read interface data."""
|
||||||
# Wait for dbus object to be available after restart
|
|
||||||
introspection: Node | None = None
|
introspection: Node | None = None
|
||||||
|
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
@ -112,93 +145,21 @@ class DBus:
|
|||||||
"Could not get introspection data after 3 attempts", _LOGGER.error
|
"Could not get introspection data after 3 attempts", _LOGGER.error
|
||||||
)
|
)
|
||||||
|
|
||||||
self._add_interfaces(introspection)
|
self._proxy_obj = self.bus.get_proxy_object(
|
||||||
|
self.bus_name, self.object_path, introspection
|
||||||
def _prepare_args(self, *args: list[Any]) -> tuple[str, list[Any]]:
|
|
||||||
signature = ""
|
|
||||||
arg_list = []
|
|
||||||
|
|
||||||
for arg in args:
|
|
||||||
_LOGGER.debug("...arg %s (type %s)", str(arg), type(arg))
|
|
||||||
if isinstance(arg, bool):
|
|
||||||
signature += "b"
|
|
||||||
arg_list.append(arg)
|
|
||||||
elif isinstance(arg, int):
|
|
||||||
signature += "i"
|
|
||||||
arg_list.append(arg)
|
|
||||||
elif isinstance(arg, float):
|
|
||||||
signature += "d"
|
|
||||||
arg_list.append(arg)
|
|
||||||
elif isinstance(arg, str):
|
|
||||||
signature += "s"
|
|
||||||
arg_list.append(arg)
|
|
||||||
elif isinstance(arg, tuple):
|
|
||||||
signature += arg[0]
|
|
||||||
arg_list.append(arg[1])
|
|
||||||
else:
|
|
||||||
raise DBusFatalError(f"Type {type(arg)} not supported")
|
|
||||||
|
|
||||||
return signature, arg_list
|
|
||||||
|
|
||||||
async def call_dbus(
|
|
||||||
self, method: str, *args: list[Any], remove_signature: bool = True
|
|
||||||
) -> str:
|
|
||||||
"""Call a dbus method."""
|
|
||||||
method_parts = method.split(".")
|
|
||||||
|
|
||||||
signature, arg_list = self._prepare_args(*args)
|
|
||||||
|
|
||||||
_LOGGER.debug("Call %s on %s", method, self.object_path)
|
|
||||||
reply = await self._bus.call(
|
|
||||||
Message(
|
|
||||||
destination=self.bus_name,
|
|
||||||
path=self.object_path,
|
|
||||||
interface=".".join(method_parts[:-1]),
|
|
||||||
member=method_parts[-1],
|
|
||||||
signature=signature,
|
|
||||||
body=arg_list,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
self._add_interfaces()
|
||||||
|
|
||||||
if reply.message_type == MessageType.ERROR:
|
@property
|
||||||
if reply.error_name == "org.freedesktop.DBus.Error.ServiceUnknown":
|
def bus(self) -> MessageBus:
|
||||||
raise DBusInterfaceError(reply.body[0])
|
"""Get message bus."""
|
||||||
if reply.error_name == "org.freedesktop.DBus.Error.UnknownMethod":
|
return self._bus
|
||||||
raise DBusInterfaceMethodError(reply.body[0])
|
|
||||||
if reply.error_name == "org.freedesktop.DBus.Error.Disconnected":
|
|
||||||
raise DBusNotConnectedError()
|
|
||||||
if reply.body and len(reply.body) > 0:
|
|
||||||
raise DBusFatalError(reply.body[0])
|
|
||||||
raise DBusFatalError()
|
|
||||||
|
|
||||||
if remove_signature:
|
|
||||||
return _remove_dbus_signature(reply.body)
|
|
||||||
return reply.body
|
|
||||||
|
|
||||||
async def get_properties(self, interface: str) -> dict[str, Any]:
|
async def get_properties(self, interface: str) -> dict[str, Any]:
|
||||||
"""Read all properties from interface."""
|
"""Read all properties from interface."""
|
||||||
try:
|
return await DBus.call_dbus(
|
||||||
return (await self.call_dbus(DBUS_METHOD_GETALL, interface))[0]
|
self._proxies[DBUS_INTERFACE_PROPERTIES], "call_get_all", interface
|
||||||
except IndexError as err:
|
)
|
||||||
_LOGGER.error("No attributes returned for %s", interface)
|
|
||||||
raise DBusFatalError() from err
|
|
||||||
|
|
||||||
async def get_property(self, interface: str, name: str) -> Any:
|
|
||||||
"""Read value of single property from interface."""
|
|
||||||
try:
|
|
||||||
return (await self.call_dbus(DBUS_METHOD_GET, interface, name))[0]
|
|
||||||
except IndexError as err:
|
|
||||||
_LOGGER.error("No attribute returned for %s on %s", name, interface)
|
|
||||||
raise DBusFatalError() from err
|
|
||||||
|
|
||||||
async def set_property(
|
|
||||||
self,
|
|
||||||
interface: str,
|
|
||||||
name: str,
|
|
||||||
value: Any,
|
|
||||||
) -> list[Any] | dict[str, Any] | None:
|
|
||||||
"""Set a property from interface."""
|
|
||||||
return await self.call_dbus(DBUS_METHOD_SET, interface, name, value)
|
|
||||||
|
|
||||||
def signal(self, signal_member: str) -> DBusSignalWrapper:
|
def signal(self, signal_member: str) -> DBusSignalWrapper:
|
||||||
"""Get signal context manager for this object."""
|
"""Get signal context manager for this object."""
|
||||||
@ -216,29 +177,52 @@ class DBusCallWrapper:
|
|||||||
"""Initialize wrapper."""
|
"""Initialize wrapper."""
|
||||||
self.dbus: DBus = dbus
|
self.dbus: DBus = dbus
|
||||||
self.interface: str = interface
|
self.interface: str = interface
|
||||||
|
self._proxy: ProxyInterface | None = self.dbus._proxies.get(self.interface)
|
||||||
|
|
||||||
def __call__(self) -> None:
|
def __call__(self) -> None:
|
||||||
"""Catch this method from being called."""
|
"""Catch this method from being called."""
|
||||||
_LOGGER.error("D-Bus method %s not exists!", self.interface)
|
_LOGGER.error("D-Bus method %s not exists!", self.interface)
|
||||||
raise DBusInterfaceMethodError()
|
raise DBusInterfaceMethodError()
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
def __getattr__(self, name: str) -> Awaitable | Callable:
|
||||||
"""Map to dbus method."""
|
"""Map to dbus method."""
|
||||||
interface = f"{self.interface}.{name}"
|
if not self._proxy:
|
||||||
|
return DBusCallWrapper(self.dbus, f"{self.interface}.{name}")
|
||||||
|
|
||||||
if interface not in self.dbus.methods:
|
dbus_type = name.split("_", 1)[0]
|
||||||
return DBusCallWrapper(self.dbus, interface)
|
|
||||||
|
|
||||||
def _method_wrapper(*args, remove_signature: bool = True):
|
if not hasattr(self._proxy, name):
|
||||||
"""Wrap method.
|
message = f"{name} does not exist in D-Bus interface {self.interface}!"
|
||||||
|
if dbus_type == "call":
|
||||||
|
raise DBusInterfaceMethodError(message, _LOGGER.error)
|
||||||
|
if dbus_type == "get":
|
||||||
|
raise DBusInterfacePropertyError(message, _LOGGER.error)
|
||||||
|
if dbus_type == "set":
|
||||||
|
raise DBusInterfacePropertyError(message, _LOGGER.error)
|
||||||
|
if dbus_type in ["on", "off"]:
|
||||||
|
raise DBusInterfaceSignalError(message, _LOGGER.error)
|
||||||
|
|
||||||
Return a coroutine
|
# Not much can be done with these currently. *args callbacks aren't supported so can't wrap it
|
||||||
"""
|
if dbus_type in ["on", "off"]:
|
||||||
return self.dbus.call_dbus(
|
_LOGGER.debug(
|
||||||
interface, *args, remove_signature=remove_signature
|
"D-Bus signal monitor - %s.%s on %s",
|
||||||
|
self.interface,
|
||||||
|
name,
|
||||||
|
self.dbus.object_path,
|
||||||
)
|
)
|
||||||
|
return self._method
|
||||||
|
|
||||||
return _method_wrapper
|
if dbus_type in ["call", "get", "set"]:
|
||||||
|
|
||||||
|
def _method_wrapper(*args, remove_signature: bool = True) -> Awaitable:
|
||||||
|
return DBus.call_dbus(
|
||||||
|
self._proxy, name, *args, remove_signature=remove_signature
|
||||||
|
)
|
||||||
|
|
||||||
|
return _method_wrapper
|
||||||
|
|
||||||
|
# Didn't reach the dbus call yet, just happened to hit another interface. Return a wrapper
|
||||||
|
return DBusCallWrapper(self.dbus, f"{self.interface}.{name}")
|
||||||
|
|
||||||
|
|
||||||
class DBusSignalWrapper:
|
class DBusSignalWrapper:
|
||||||
|
@ -201,19 +201,15 @@ async def test_api_network_wireless_scan(api_client):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_api_network_reload(api_client, coresys):
|
async def test_api_network_reload(api_client, coresys, dbus: list[str]):
|
||||||
"""Test network manager reload api."""
|
"""Test network manager reload api."""
|
||||||
with patch.object(type(coresys.dbus.network.dbus), "call_dbus") as call_dbus:
|
dbus.clear()
|
||||||
resp = await api_client.post("/network/reload")
|
resp = await api_client.post("/network/reload")
|
||||||
result = await resp.json()
|
result = await resp.json()
|
||||||
|
|
||||||
assert result["result"] == "ok"
|
assert result["result"] == "ok"
|
||||||
assert (
|
# Check that we forced NM to do an immediate connectivity check
|
||||||
call_dbus.call_args_list[0][0][0]
|
assert (
|
||||||
== "org.freedesktop.NetworkManager.Settings.Connection.GetSettings"
|
"/org/freedesktop/NetworkManager-org.freedesktop.NetworkManager.CheckConnectivity"
|
||||||
)
|
in dbus
|
||||||
# Check that we forced NM to do an immediate connectivity check
|
)
|
||||||
assert (
|
|
||||||
call_dbus.call_args_list[1][0][0]
|
|
||||||
== "org.freedesktop.NetworkManager.CheckConnectivity"
|
|
||||||
)
|
|
||||||
|
@ -3,14 +3,14 @@ from functools import partial
|
|||||||
from inspect import unwrap
|
from inspect import unwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
from typing import Any
|
|
||||||
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
|
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
from dbus_next import introspection as intr
|
|
||||||
from dbus_next.aio.message_bus import MessageBus
|
from dbus_next.aio.message_bus import MessageBus
|
||||||
|
from dbus_next.aio.proxy_object import ProxyInterface, ProxyObject
|
||||||
|
from dbus_next.introspection import Method, Property, Signal
|
||||||
import pytest
|
import pytest
|
||||||
from securetar import SecureTarFile
|
from securetar import SecureTarFile
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ from supervisor.docker.manager import DockerAPI
|
|||||||
from supervisor.docker.monitor import DockerMonitor
|
from supervisor.docker.monitor import DockerMonitor
|
||||||
from supervisor.store.addon import AddonStore
|
from supervisor.store.addon import AddonStore
|
||||||
from supervisor.store.repository import Repository
|
from supervisor.store.repository import Repository
|
||||||
from supervisor.utils.dbus import DBus
|
from supervisor.utils.dbus import DBUS_INTERFACE_PROPERTIES, DBus
|
||||||
from supervisor.utils.dt import utcnow
|
from supervisor.utils.dt import utcnow
|
||||||
|
|
||||||
from .common import exists_fixture, load_fixture, load_json_fixture
|
from .common import exists_fixture, load_fixture, load_json_fixture
|
||||||
@ -103,10 +103,39 @@ def docker() -> DockerAPI:
|
|||||||
yield docker_obj
|
yield docker_obj
|
||||||
|
|
||||||
|
|
||||||
|
def _get_dbus_name(intr_list: list[Method | Property | Signal], snake_case: str) -> str:
|
||||||
|
"""Find name in introspection list, fallback to ignore case match."""
|
||||||
|
name = "".join([part.capitalize() for part in snake_case.split("_")])
|
||||||
|
names = [item.name for item in intr_list]
|
||||||
|
if name in names:
|
||||||
|
return name
|
||||||
|
|
||||||
|
# Acronyms like NTP can't be easily converted back to camel case. Fallback to ignore case match
|
||||||
|
lower_name = name.lower()
|
||||||
|
for val in names:
|
||||||
|
if lower_name == val.lower():
|
||||||
|
return val
|
||||||
|
|
||||||
|
raise AttributeError(f"Could not find match for {name} in D-Bus introspection!")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def dbus_bus() -> MessageBus:
|
async def dbus_bus() -> MessageBus:
|
||||||
"""Message bus mock."""
|
"""Message bus mock."""
|
||||||
yield AsyncMock(spec=MessageBus)
|
bus = AsyncMock(spec=MessageBus)
|
||||||
|
setattr(bus, "_name_owners", {})
|
||||||
|
yield bus
|
||||||
|
|
||||||
|
|
||||||
|
def mock_get_properties(object_path: str, interface: str) -> str:
|
||||||
|
"""Mock get dbus properties."""
|
||||||
|
latest = object_path.split("/")[-1]
|
||||||
|
fixture = interface.replace(".", "_")
|
||||||
|
|
||||||
|
if latest.isnumeric():
|
||||||
|
fixture = f"{fixture}_{latest}"
|
||||||
|
|
||||||
|
return load_json_fixture(f"{fixture}.json")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -114,20 +143,6 @@ def dbus(dbus_bus: MessageBus) -> DBus:
|
|||||||
"""Mock DBUS."""
|
"""Mock DBUS."""
|
||||||
dbus_commands = []
|
dbus_commands = []
|
||||||
|
|
||||||
async def mock_get_properties(dbus_obj, interface):
|
|
||||||
latest = dbus_obj.object_path.split("/")[-1]
|
|
||||||
fixture = interface.replace(".", "_")
|
|
||||||
|
|
||||||
if latest.isnumeric():
|
|
||||||
fixture = f"{fixture}_{latest}"
|
|
||||||
|
|
||||||
return load_json_fixture(f"{fixture}.json")
|
|
||||||
|
|
||||||
async def mock_get_property(dbus_obj, interface, name):
|
|
||||||
dbus_commands.append(f"{dbus_obj.object_path}-{interface}.{name}")
|
|
||||||
properties = await mock_get_properties(dbus_obj, interface)
|
|
||||||
return properties[name]
|
|
||||||
|
|
||||||
async def mock_wait_for_signal(self):
|
async def mock_wait_for_signal(self):
|
||||||
if (
|
if (
|
||||||
self._interface + "." + self._member
|
self._interface + "." + self._member
|
||||||
@ -161,41 +176,69 @@ def dbus(dbus_bus: MessageBus) -> DBus:
|
|||||||
fixture = f"{fixture}_~"
|
fixture = f"{fixture}_~"
|
||||||
|
|
||||||
# Use dbus-next infrastructure to parse introspection xml
|
# Use dbus-next infrastructure to parse introspection xml
|
||||||
node = intr.Node.parse(load_fixture(f"{fixture}.{filetype}"))
|
self._proxy_obj = ProxyObject(
|
||||||
self._add_interfaces(node)
|
self.bus_name,
|
||||||
|
self.object_path,
|
||||||
|
load_fixture(f"{fixture}.{filetype}"),
|
||||||
|
dbus_bus,
|
||||||
|
)
|
||||||
|
self._add_interfaces()
|
||||||
|
|
||||||
async def mock_call_dbus(
|
async def mock_call_dbus(
|
||||||
self, method: str, *args: list[Any], remove_signature: bool = True
|
proxy_interface: ProxyInterface,
|
||||||
|
method: str,
|
||||||
|
*args,
|
||||||
|
remove_signature: bool = True,
|
||||||
):
|
):
|
||||||
if self.object_path != DBUS_OBJECT_BASE:
|
if (
|
||||||
fixture = self.object_path.replace("/", "_")[1:]
|
proxy_interface.introspection.name == DBUS_INTERFACE_PROPERTIES
|
||||||
fixture = f"{fixture}-{method.split('.')[-1]}"
|
and method == "call_get_all"
|
||||||
else:
|
):
|
||||||
fixture = method.replace(".", "_")
|
return mock_get_properties(proxy_interface.path, args[0])
|
||||||
|
|
||||||
dbus_commands.append(f"{self.object_path}-{method}")
|
[dbus_type, dbus_name] = method.split("_", 1)
|
||||||
|
|
||||||
|
if dbus_type in ["get", "set"]:
|
||||||
|
dbus_name = _get_dbus_name(
|
||||||
|
proxy_interface.introspection.properties, dbus_name
|
||||||
|
)
|
||||||
|
dbus_commands.append(
|
||||||
|
f"{proxy_interface.path}-{proxy_interface.introspection.name}.{dbus_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if dbus_type == "set":
|
||||||
|
return
|
||||||
|
|
||||||
|
return mock_get_properties(
|
||||||
|
proxy_interface.path, proxy_interface.introspection.name
|
||||||
|
)[dbus_name]
|
||||||
|
|
||||||
|
dbus_name = _get_dbus_name(proxy_interface.introspection.methods, dbus_name)
|
||||||
|
dbus_commands.append(
|
||||||
|
f"{proxy_interface.path}-{proxy_interface.introspection.name}.{dbus_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if proxy_interface.path != DBUS_OBJECT_BASE:
|
||||||
|
fixture = proxy_interface.path.replace("/", "_")[1:]
|
||||||
|
fixture = f"{fixture}-{dbus_name}"
|
||||||
|
else:
|
||||||
|
fixture = (
|
||||||
|
f'{proxy_interface.introspection.name.replace(".", "_")}_{dbus_name}'
|
||||||
|
)
|
||||||
|
|
||||||
if exists_fixture(f"{fixture}.json"):
|
if exists_fixture(f"{fixture}.json"):
|
||||||
return load_json_fixture(f"{fixture}.json")
|
return load_json_fixture(f"{fixture}.json")
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
with patch("supervisor.utils.dbus.DBus.call_dbus", new=mock_call_dbus), patch(
|
with patch("supervisor.utils.dbus.DBus.call_dbus", new=mock_call_dbus), patch(
|
||||||
"supervisor.dbus.interface.DBusInterface.is_connected",
|
"supervisor.dbus.interface.DBusInterface.is_connected",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
), patch(
|
), patch("supervisor.utils.dbus.DBus._init_proxy", new=mock_init_proxy), patch(
|
||||||
"supervisor.utils.dbus.DBus.get_properties", new=mock_get_properties
|
|
||||||
), patch(
|
|
||||||
"supervisor.utils.dbus.DBus._init_proxy", new=mock_init_proxy
|
|
||||||
), patch(
|
|
||||||
"supervisor.utils.dbus.DBusSignalWrapper.__aenter__", new=mock_signal___aenter__
|
"supervisor.utils.dbus.DBusSignalWrapper.__aenter__", new=mock_signal___aenter__
|
||||||
), patch(
|
), patch(
|
||||||
"supervisor.utils.dbus.DBusSignalWrapper.__aexit__", new=mock_signal___aexit__
|
"supervisor.utils.dbus.DBusSignalWrapper.__aexit__", new=mock_signal___aexit__
|
||||||
), patch(
|
), patch(
|
||||||
"supervisor.utils.dbus.DBusSignalWrapper.wait_for_signal",
|
"supervisor.utils.dbus.DBusSignalWrapper.wait_for_signal",
|
||||||
new=mock_wait_for_signal,
|
new=mock_wait_for_signal,
|
||||||
), patch(
|
|
||||||
"supervisor.utils.dbus.DBus.get_property", new=mock_get_property
|
|
||||||
), patch(
|
), patch(
|
||||||
"supervisor.dbus.manager.MessageBus.connect", return_value=dbus_bus
|
"supervisor.dbus.manager.MessageBus.connect", return_value=dbus_bus
|
||||||
):
|
):
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from dbus_next.aio.proxy_object import ProxyInterface
|
||||||
from dbus_next.signature import Variant
|
from dbus_next.signature import Variant
|
||||||
|
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
from supervisor.dbus.network.setting.generate import get_connection_from_interface
|
from supervisor.dbus.network.setting.generate import get_connection_from_interface
|
||||||
from supervisor.host.const import InterfaceMethod
|
from supervisor.host.const import InterfaceMethod
|
||||||
from supervisor.host.network import Interface
|
from supervisor.host.network import Interface
|
||||||
|
from supervisor.utils.dbus import DBus
|
||||||
|
|
||||||
from tests.const import TEST_INTERFACE
|
from tests.const import TEST_INTERFACE
|
||||||
|
|
||||||
@ -68,19 +70,14 @@ SETTINGS_WITH_SIGNATURE = {
|
|||||||
|
|
||||||
|
|
||||||
async def mock_call_dbus_get_settings_signature(
|
async def mock_call_dbus_get_settings_signature(
|
||||||
method: str, *args: list[Any], remove_signature: bool = True
|
_: ProxyInterface, method: str, *args, remove_signature: bool = True
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
"""Call dbus method mock for get settings that keeps signature."""
|
"""Call dbus method mock for get settings that keeps signature."""
|
||||||
if (
|
if method == "call_get_settings" and not remove_signature:
|
||||||
method == "org.freedesktop.NetworkManager.Settings.Connection.GetSettings"
|
return SETTINGS_WITH_SIGNATURE
|
||||||
and not remove_signature
|
|
||||||
):
|
|
||||||
return [SETTINGS_WITH_SIGNATURE]
|
|
||||||
else:
|
else:
|
||||||
assert method == "org.freedesktop.NetworkManager.Settings.Connection.Update"
|
assert method == "call_update"
|
||||||
assert len(args[0]) == 2
|
settings = args[0]
|
||||||
assert args[0][0] == "a{sa{sv}}"
|
|
||||||
settings = args[0][1]
|
|
||||||
|
|
||||||
assert "connection" in settings
|
assert "connection" in settings
|
||||||
assert settings["connection"]["id"] == Variant("s", "Supervisor eth0")
|
assert settings["connection"]["id"] == Variant("s", "Supervisor eth0")
|
||||||
@ -145,7 +142,7 @@ async def test_update(coresys: CoreSys):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
coresys.dbus.network.interfaces[TEST_INTERFACE].settings.dbus,
|
DBus,
|
||||||
"call_dbus",
|
"call_dbus",
|
||||||
new=mock_call_dbus_get_settings_signature,
|
new=mock_call_dbus_get_settings_signature,
|
||||||
):
|
):
|
||||||
|
@ -1,110 +1,108 @@
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
[
|
"kernel.0",
|
||||||
"kernel.0",
|
{
|
||||||
{
|
"activated.count": 9,
|
||||||
"activated.count": 9,
|
"activated.timestamp": "2022-08-23T21:03:22Z",
|
||||||
"activated.timestamp": "2022-08-23T21:03:22Z",
|
"boot-status": "good",
|
||||||
"boot-status": "good",
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"sha256": "c624db648b8401fae37ee5bb1a6ec90bdf4183aef364b33314a73c7198e49d5b",
|
||||||
"sha256": "c624db648b8401fae37ee5bb1a6ec90bdf4183aef364b33314a73c7198e49d5b",
|
"state": "inactive",
|
||||||
"state": "inactive",
|
"size": 10371072,
|
||||||
"size": 10371072,
|
"installed.count": 9,
|
||||||
"installed.count": 9,
|
"class": "kernel",
|
||||||
"class": "kernel",
|
"device": "/dev/disk/by-partlabel/hassos-kernel0",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-kernel0",
|
"type": "raw",
|
||||||
"type": "raw",
|
"bootname": "A",
|
||||||
"bootname": "A",
|
"bundle.version": "9.0.dev20220818",
|
||||||
"bundle.version": "9.0.dev20220818",
|
"installed.timestamp": "2022-08-23T21:03:16Z",
|
||||||
"installed.timestamp": "2022-08-23T21:03:16Z",
|
"status": "ok"
|
||||||
"status": "ok"
|
}
|
||||||
}
|
],
|
||||||
],
|
[
|
||||||
[
|
"boot.0",
|
||||||
"boot.0",
|
{
|
||||||
{
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"sha256": "a5019b335f33be2cf89c96bb2d0695030adb72c1d13d650a5bbe1806dd76d6cc",
|
||||||
"sha256": "a5019b335f33be2cf89c96bb2d0695030adb72c1d13d650a5bbe1806dd76d6cc",
|
"state": "inactive",
|
||||||
"state": "inactive",
|
"size": 25165824,
|
||||||
"size": 25165824,
|
"installed.count": 19,
|
||||||
"installed.count": 19,
|
"class": "boot",
|
||||||
"class": "boot",
|
"device": "/dev/disk/by-partlabel/hassos-boot",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-boot",
|
"type": "vfat",
|
||||||
"type": "vfat",
|
"status": "ok",
|
||||||
"status": "ok",
|
"bundle.version": "9.0.dev20220824",
|
||||||
"bundle.version": "9.0.dev20220824",
|
"installed.timestamp": "2022-08-25T21:11:46Z"
|
||||||
"installed.timestamp": "2022-08-25T21:11:46Z"
|
}
|
||||||
}
|
],
|
||||||
],
|
[
|
||||||
[
|
"rootfs.0",
|
||||||
"rootfs.0",
|
{
|
||||||
{
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"parent": "kernel.0",
|
||||||
"parent": "kernel.0",
|
"state": "inactive",
|
||||||
"state": "inactive",
|
"size": 117456896,
|
||||||
"size": 117456896,
|
"sha256": "7d908b4d578d072b1b0f75de8250fd97b6e119bff09518a96fffd6e4aec61721",
|
||||||
"sha256": "7d908b4d578d072b1b0f75de8250fd97b6e119bff09518a96fffd6e4aec61721",
|
"class": "rootfs",
|
||||||
"class": "rootfs",
|
"device": "/dev/disk/by-partlabel/hassos-system0",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-system0",
|
"type": "raw",
|
||||||
"type": "raw",
|
"status": "ok",
|
||||||
"status": "ok",
|
"bundle.version": "9.0.dev20220818",
|
||||||
"bundle.version": "9.0.dev20220818",
|
"installed.timestamp": "2022-08-23T21:03:21Z",
|
||||||
"installed.timestamp": "2022-08-23T21:03:21Z",
|
"installed.count": 9
|
||||||
"installed.count": 9
|
}
|
||||||
}
|
],
|
||||||
],
|
[
|
||||||
[
|
"spl.0",
|
||||||
"spl.0",
|
{
|
||||||
{
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"sha256": "9856a94df1d6abbc672adaf95746ec76abd3a8191f9d08288add6bb39e63ef45",
|
||||||
"sha256": "9856a94df1d6abbc672adaf95746ec76abd3a8191f9d08288add6bb39e63ef45",
|
"state": "inactive",
|
||||||
"state": "inactive",
|
"size": 8388608,
|
||||||
"size": 8388608,
|
"installed.count": 19,
|
||||||
"installed.count": 19,
|
"class": "spl",
|
||||||
"class": "spl",
|
"device": "/dev/disk/by-partlabel/hassos-boot",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-boot",
|
"type": "raw",
|
||||||
"type": "raw",
|
"status": "ok",
|
||||||
"status": "ok",
|
"bundle.version": "9.0.dev20220824",
|
||||||
"bundle.version": "9.0.dev20220824",
|
"installed.timestamp": "2022-08-25T21:11:51Z"
|
||||||
"installed.timestamp": "2022-08-25T21:11:51Z"
|
}
|
||||||
}
|
],
|
||||||
],
|
[
|
||||||
[
|
"kernel.1",
|
||||||
"kernel.1",
|
{
|
||||||
{
|
"activated.count": 10,
|
||||||
"activated.count": 10,
|
"activated.timestamp": "2022-08-25T21:11:52Z",
|
||||||
"activated.timestamp": "2022-08-25T21:11:52Z",
|
"boot-status": "good",
|
||||||
"boot-status": "good",
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"sha256": "f57e354b8bd518022721e71fafaf278972af966d8f6cbefb4610db13785801c8",
|
||||||
"sha256": "f57e354b8bd518022721e71fafaf278972af966d8f6cbefb4610db13785801c8",
|
"state": "booted",
|
||||||
"state": "booted",
|
"size": 10371072,
|
||||||
"size": 10371072,
|
"installed.count": 10,
|
||||||
"installed.count": 10,
|
"class": "kernel",
|
||||||
"class": "kernel",
|
"device": "/dev/disk/by-partlabel/hassos-kernel1",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-kernel1",
|
"type": "raw",
|
||||||
"type": "raw",
|
"bootname": "B",
|
||||||
"bootname": "B",
|
"bundle.version": "9.0.dev20220824",
|
||||||
"bundle.version": "9.0.dev20220824",
|
"installed.timestamp": "2022-08-25T21:11:46Z",
|
||||||
"installed.timestamp": "2022-08-25T21:11:46Z",
|
"status": "ok"
|
||||||
"status": "ok"
|
}
|
||||||
}
|
],
|
||||||
],
|
[
|
||||||
[
|
"rootfs.1",
|
||||||
"rootfs.1",
|
{
|
||||||
{
|
"bundle.compatible": "haos-odroid-n2",
|
||||||
"bundle.compatible": "haos-odroid-n2",
|
"parent": "kernel.1",
|
||||||
"parent": "kernel.1",
|
"state": "active",
|
||||||
"state": "active",
|
"size": 117456896,
|
||||||
"size": 117456896,
|
"sha256": "55936b64d391954ae1aed24dd1460e191e021e78655470051fa7939d12fff68a",
|
||||||
"sha256": "55936b64d391954ae1aed24dd1460e191e021e78655470051fa7939d12fff68a",
|
"class": "rootfs",
|
||||||
"class": "rootfs",
|
"device": "/dev/disk/by-partlabel/hassos-system1",
|
||||||
"device": "/dev/disk/by-partlabel/hassos-system1",
|
"type": "raw",
|
||||||
"type": "raw",
|
"status": "ok",
|
||||||
"status": "ok",
|
"bundle.version": "9.0.dev20220824",
|
||||||
"bundle.version": "9.0.dev20220824",
|
"installed.timestamp": "2022-08-25T21:11:51Z",
|
||||||
"installed.timestamp": "2022-08-25T21:11:51Z",
|
"installed.count": 10
|
||||||
"installed.count": 10
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/NetworkManager/ActiveConnection/1"]
|
"/org/freedesktop/NetworkManager/ActiveConnection/1"
|
||||||
|
@ -1 +1 @@
|
|||||||
[4]
|
4
|
||||||
|
@ -1 +1,4 @@
|
|||||||
[["/org/freedesktop/NetworkManager/AccessPoint/43099", "/org/freedesktop/NetworkManager/AccessPoint/43100"]]
|
[
|
||||||
|
"/org/freedesktop/NetworkManager/AccessPoint/43099",
|
||||||
|
"/org/freedesktop/NetworkManager/AccessPoint/43100"
|
||||||
|
]
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/NetworkManager/Settings/1"]
|
"/org/freedesktop/NetworkManager/Settings/1"
|
||||||
|
@ -1 +1 @@
|
|||||||
[true]
|
true
|
||||||
|
@ -1,41 +1,39 @@
|
|||||||
[
|
{
|
||||||
{
|
"connection": {
|
||||||
"connection": {
|
"id": "Wired connection 1",
|
||||||
"id": "Wired connection 1",
|
"interface-name": "eth0",
|
||||||
"interface-name": "eth0",
|
"permissions": [],
|
||||||
"permissions": [],
|
"timestamp": 1598125548,
|
||||||
"timestamp": 1598125548,
|
"type": "802-3-ethernet",
|
||||||
"type": "802-3-ethernet",
|
"uuid": "0c23631e-2118-355c-bbb0-8943229cb0d6"
|
||||||
"uuid": "0c23631e-2118-355c-bbb0-8943229cb0d6"
|
},
|
||||||
},
|
"ipv4": {
|
||||||
"ipv4": {
|
"address-data": [{ "address": "192.168.2.148", "prefix": 24 }],
|
||||||
"address-data": [{ "address": "192.168.2.148", "prefix": 24 }],
|
"addresses": [[2483202240, 24, 16951488]],
|
||||||
"addresses": [[2483202240, 24, 16951488]],
|
"dns": [16951488],
|
||||||
"dns": [16951488],
|
"dns-search": [],
|
||||||
"dns-search": [],
|
"gateway": "192.168.2.1",
|
||||||
"gateway": "192.168.2.1",
|
"method": "auto",
|
||||||
"method": "auto",
|
"route-data": [
|
||||||
"route-data": [
|
{ "dest": "192.168.122.0", "prefix": 24, "next-hop": "10.10.10.1" }
|
||||||
{ "dest": "192.168.122.0", "prefix": 24, "next-hop": "10.10.10.1" }
|
],
|
||||||
],
|
"routes": [[8038592, 24, 17435146, 0]]
|
||||||
"routes": [[8038592, 24, 17435146, 0]]
|
},
|
||||||
},
|
"ipv6": {
|
||||||
"ipv6": {
|
"address-data": [],
|
||||||
"address-data": [],
|
"addresses": [],
|
||||||
"addresses": [],
|
"dns": [],
|
||||||
"dns": [],
|
"dns-search": [],
|
||||||
"dns-search": [],
|
"method": "auto",
|
||||||
"method": "auto",
|
"route-data": [],
|
||||||
"route-data": [],
|
"routes": [],
|
||||||
"routes": [],
|
"addr-gen-mode": 0
|
||||||
"addr-gen-mode": 0
|
},
|
||||||
},
|
"proxy": {},
|
||||||
"proxy": {},
|
"802-3-ethernet": {
|
||||||
"802-3-ethernet": {
|
"auto-negotiate": false,
|
||||||
"auto-negotiate": false,
|
"mac-address-blacklist": [],
|
||||||
"mac-address-blacklist": [],
|
"s390-options": {}
|
||||||
"s390-options": {}
|
},
|
||||||
},
|
"802-11-wireless": { "ssid": [78, 69, 84, 84] }
|
||||||
"802-11-wireless": { "ssid": [78, 69, 84, 84] }
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
|
@ -1,52 +1,50 @@
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
[
|
"etc-machine\\x2did.mount",
|
||||||
"etc-machine\\x2did.mount",
|
"/etc/machine-id",
|
||||||
"/etc/machine-id",
|
"loaded",
|
||||||
"loaded",
|
"active",
|
||||||
"active",
|
"mounted",
|
||||||
"mounted",
|
"",
|
||||||
"",
|
"/org/freedesktop/systemd1/unit/etc_2dmachine_5cx2did_2emount",
|
||||||
"/org/freedesktop/systemd1/unit/etc_2dmachine_5cx2did_2emount",
|
0,
|
||||||
0,
|
"",
|
||||||
"",
|
"/"
|
||||||
"/"
|
],
|
||||||
],
|
[
|
||||||
[
|
"firewalld.service",
|
||||||
"firewalld.service",
|
"firewalld.service",
|
||||||
"firewalld.service",
|
"not-found",
|
||||||
"not-found",
|
"inactive",
|
||||||
"inactive",
|
"dead",
|
||||||
"dead",
|
"",
|
||||||
"",
|
"/org/freedesktop/systemd1/unit/firewalld_2eservice",
|
||||||
"/org/freedesktop/systemd1/unit/firewalld_2eservice",
|
0,
|
||||||
0,
|
"",
|
||||||
"",
|
"/"
|
||||||
"/"
|
],
|
||||||
],
|
[
|
||||||
[
|
"sys-devices-virtual-tty-ttypd.device",
|
||||||
"sys-devices-virtual-tty-ttypd.device",
|
"/sys/devices/virtual/tty/ttypd",
|
||||||
"/sys/devices/virtual/tty/ttypd",
|
"loaded",
|
||||||
"loaded",
|
"active",
|
||||||
"active",
|
"plugged",
|
||||||
"plugged",
|
"",
|
||||||
"",
|
"/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dtty_2dttypd_2edevice",
|
||||||
"/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dtty_2dttypd_2edevice",
|
0,
|
||||||
0,
|
"",
|
||||||
"",
|
"/"
|
||||||
"/"
|
],
|
||||||
],
|
[
|
||||||
[
|
"zram-swap.service",
|
||||||
"zram-swap.service",
|
"HassOS ZRAM swap",
|
||||||
"HassOS ZRAM swap",
|
"loaded",
|
||||||
"loaded",
|
"active",
|
||||||
"active",
|
"exited",
|
||||||
"exited",
|
"",
|
||||||
"",
|
"/org/freedesktop/systemd1/unit/zram_2dswap_2eservice",
|
||||||
"/org/freedesktop/systemd1/unit/zram_2dswap_2eservice",
|
0,
|
||||||
0,
|
"",
|
||||||
"",
|
"/"
|
||||||
"/"
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/systemd1/job/7623"]
|
"/org/freedesktop/systemd1/job/7623"
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/systemd1/job/7623"]
|
"/org/freedesktop/systemd1/job/7623"
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/systemd1/job/7623"]
|
"/org/freedesktop/systemd1/job/7623"
|
||||||
|
@ -1 +1 @@
|
|||||||
["/org/freedesktop/systemd1/job/7623"]
|
"/org/freedesktop/systemd1/job/7623"
|
||||||
|
@ -10,33 +10,29 @@ from supervisor.coresys import CoreSys
|
|||||||
|
|
||||||
async def test_connectivity_not_connected(coresys: CoreSys):
|
async def test_connectivity_not_connected(coresys: CoreSys):
|
||||||
"""Test host unknown connectivity."""
|
"""Test host unknown connectivity."""
|
||||||
with patch("supervisor.utils.dbus.DBus.get_property", return_value=0):
|
with patch("supervisor.utils.dbus.DBus.call_dbus", return_value=0):
|
||||||
await coresys.host.network.check_connectivity()
|
await coresys.host.network.check_connectivity()
|
||||||
assert not coresys.host.network.connectivity
|
assert not coresys.host.network.connectivity
|
||||||
|
|
||||||
with patch("supervisor.utils.dbus.DBus.call_dbus", return_value=[0]):
|
|
||||||
await coresys.host.network.check_connectivity(force=True)
|
await coresys.host.network.check_connectivity(force=True)
|
||||||
assert not coresys.host.network.connectivity
|
assert not coresys.host.network.connectivity
|
||||||
|
|
||||||
|
|
||||||
async def test_connectivity_connected(coresys: CoreSys):
|
async def test_connectivity_connected(coresys: CoreSys, dbus: list[str]):
|
||||||
"""Test host full connectivity."""
|
"""Test host full connectivity."""
|
||||||
# Variation on above since our default fixture for each of these returns 4
|
dbus.clear()
|
||||||
with patch(
|
await coresys.host.network.check_connectivity()
|
||||||
"supervisor.utils.dbus.DBus.get_property", return_value=4
|
assert coresys.host.network.connectivity
|
||||||
) as get_property, patch(
|
assert dbus == [
|
||||||
"supervisor.utils.dbus.DBus.call_dbus", return_value=[4]
|
"/org/freedesktop/NetworkManager-org.freedesktop.NetworkManager.Connectivity"
|
||||||
) as call_dbus:
|
]
|
||||||
await coresys.host.network.check_connectivity()
|
|
||||||
assert coresys.host.network.connectivity
|
|
||||||
get_property.assert_called_once()
|
|
||||||
call_dbus.assert_not_called()
|
|
||||||
|
|
||||||
get_property.reset_mock()
|
dbus.clear()
|
||||||
await coresys.host.network.check_connectivity(force=True)
|
await coresys.host.network.check_connectivity(force=True)
|
||||||
assert coresys.host.network.connectivity
|
assert coresys.host.network.connectivity
|
||||||
get_property.assert_not_called()
|
assert dbus == [
|
||||||
call_dbus.assert_called_once()
|
"/org/freedesktop/NetworkManager-org.freedesktop.NetworkManager.CheckConnectivity"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("force", [True, False])
|
@pytest.mark.parametrize("force", [True, False])
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from ipaddress import IPv4Address, IPv6Address
|
from ipaddress import IPv4Address, IPv6Address
|
||||||
from unittest.mock import Mock, PropertyMock, patch
|
from unittest.mock import Mock, PropertyMock, patch
|
||||||
|
|
||||||
|
from dbus_next.aio.proxy_object import ProxyInterface
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
@ -127,24 +128,34 @@ async def test_scan_wifi(coresys: CoreSys):
|
|||||||
async def test_scan_wifi_with_failures(coresys: CoreSys, caplog):
|
async def test_scan_wifi_with_failures(coresys: CoreSys, caplog):
|
||||||
"""Test scanning wifi with accesspoint processing failures."""
|
"""Test scanning wifi with accesspoint processing failures."""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
init_proxy = coresys.dbus.network.dbus._init_proxy
|
init_proxy = DBus._init_proxy
|
||||||
|
call_dbus = DBus.call_dbus
|
||||||
|
|
||||||
async def mock_init_proxy(self):
|
async def mock_init_proxy(self):
|
||||||
if self.object_path != "/org/freedesktop/NetworkManager/AccessPoint/99999":
|
if self.object_path != "/org/freedesktop/NetworkManager/AccessPoint/99999":
|
||||||
return await init_proxy()
|
return await init_proxy(self)
|
||||||
|
|
||||||
raise DBusFatalError("Fail")
|
raise DBusFatalError("Fail")
|
||||||
|
|
||||||
with patch("supervisor.host.network.asyncio.sleep"), patch.object(
|
async def mock_call_dbus(
|
||||||
DBus,
|
proxy_interface: ProxyInterface,
|
||||||
"call_dbus",
|
method: str,
|
||||||
return_value=[
|
*args,
|
||||||
[
|
remove_signature: bool = True,
|
||||||
|
):
|
||||||
|
if method == "call_get_all_access_points":
|
||||||
|
return [
|
||||||
"/org/freedesktop/NetworkManager/AccessPoint/43099",
|
"/org/freedesktop/NetworkManager/AccessPoint/43099",
|
||||||
"/org/freedesktop/NetworkManager/AccessPoint/43100",
|
"/org/freedesktop/NetworkManager/AccessPoint/43100",
|
||||||
"/org/freedesktop/NetworkManager/AccessPoint/99999",
|
"/org/freedesktop/NetworkManager/AccessPoint/99999",
|
||||||
]
|
]
|
||||||
],
|
|
||||||
|
return await call_dbus(
|
||||||
|
proxy_interface, method, *args, remove_signature=remove_signature
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("supervisor.host.network.asyncio.sleep"), patch(
|
||||||
|
"supervisor.utils.dbus.DBus.call_dbus", new=mock_call_dbus
|
||||||
), patch.object(DBus, "_init_proxy", new=mock_init_proxy):
|
), patch.object(DBus, "_init_proxy", new=mock_init_proxy):
|
||||||
aps = await coresys.host.network.scan_wifi(coresys.host.network.get("wlan0"))
|
aps = await coresys.host.network.scan_wifi(coresys.host.network.get("wlan0"))
|
||||||
assert len(aps) == 2
|
assert len(aps) == 2
|
||||||
|
@ -85,8 +85,8 @@ async def test_internet(
|
|||||||
mock_websession = AsyncMock()
|
mock_websession = AsyncMock()
|
||||||
mock_websession.head.side_effect = head_side_effect
|
mock_websession.head.side_effect = head_side_effect
|
||||||
coresys.supervisor.connectivity = None
|
coresys.supervisor.connectivity = None
|
||||||
with patch.object(
|
with patch(
|
||||||
type(coresys.dbus.network.dbus), "get_property", return_value=connectivity
|
"supervisor.utils.dbus.DBus.call_dbus", return_value=connectivity
|
||||||
), patch.object(
|
), patch.object(
|
||||||
CoreSys, "websession", new=PropertyMock(return_value=mock_websession)
|
CoreSys, "websession", new=PropertyMock(return_value=mock_websession)
|
||||||
):
|
):
|
||||||
|
@ -1,32 +1,19 @@
|
|||||||
"""Check dbus-next implementation."""
|
"""Check dbus-next implementation."""
|
||||||
from dbus_next.signature import Variant
|
from dbus_next.signature import Variant
|
||||||
|
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.utils.dbus import DBus
|
||||||
from supervisor.utils.dbus import DBus, _remove_dbus_signature
|
|
||||||
|
|
||||||
|
|
||||||
def test_remove_dbus_signature():
|
def test_remove_dbus_signature():
|
||||||
"""Check D-Bus signature clean-up."""
|
"""Check D-Bus signature clean-up."""
|
||||||
test = _remove_dbus_signature(Variant("s", "Value"))
|
test = DBus.remove_dbus_signature(Variant("s", "Value"))
|
||||||
assert isinstance(test, str)
|
assert isinstance(test, str)
|
||||||
assert test == "Value"
|
assert test == "Value"
|
||||||
|
|
||||||
test_dict = _remove_dbus_signature({"Key": Variant("s", "Value")})
|
test_dict = DBus.remove_dbus_signature({"Key": Variant("s", "Value")})
|
||||||
assert isinstance(test_dict["Key"], str)
|
assert isinstance(test_dict["Key"], str)
|
||||||
assert test_dict["Key"] == "Value"
|
assert test_dict["Key"] == "Value"
|
||||||
|
|
||||||
test_dict = _remove_dbus_signature([Variant("s", "Value")])
|
test_dict = DBus.remove_dbus_signature([Variant("s", "Value")])
|
||||||
assert isinstance(test_dict[0], str)
|
assert isinstance(test_dict[0], str)
|
||||||
assert test_dict[0] == "Value"
|
assert test_dict[0] == "Value"
|
||||||
|
|
||||||
|
|
||||||
async def test_dbus_prepare_args(coresys: CoreSys):
|
|
||||||
"""Check D-Bus dynamic argument builder."""
|
|
||||||
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}"
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user