mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 13:46:31 +00:00
Use udev path instead of mac or name for nm match (#4476)
This commit is contained in:
parent
a98334ede8
commit
86f004e45a
@ -277,6 +277,7 @@ class APINetwork(CoreSysAttributes):
|
|||||||
)
|
)
|
||||||
|
|
||||||
vlan_interface = Interface(
|
vlan_interface = Interface(
|
||||||
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
True,
|
True,
|
||||||
|
@ -144,6 +144,7 @@ DBUS_ATTR_OPERATION = "Operation"
|
|||||||
DBUS_ATTR_OPTIONS = "Options"
|
DBUS_ATTR_OPTIONS = "Options"
|
||||||
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
|
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
|
||||||
DBUS_ATTR_PARTITIONS = "Partitions"
|
DBUS_ATTR_PARTITIONS = "Partitions"
|
||||||
|
DBUS_ATTR_PATH = "Path"
|
||||||
DBUS_ATTR_POWER_LED = "PowerLED"
|
DBUS_ATTR_POWER_LED = "PowerLED"
|
||||||
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
|
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
|
||||||
DBUS_ATTR_READ_ONLY = "ReadOnly"
|
DBUS_ATTR_READ_ONLY = "ReadOnly"
|
||||||
|
@ -66,7 +66,7 @@ class IpProperties:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class DeviceProperties:
|
class MatchProperties:
|
||||||
"""Device properties object for Network Manager."""
|
"""Match properties object for Network Manager."""
|
||||||
|
|
||||||
match_device: str | None
|
path: list[str] | None = None
|
||||||
|
@ -11,6 +11,7 @@ from ..const import (
|
|||||||
DBUS_ATTR_DRIVER,
|
DBUS_ATTR_DRIVER,
|
||||||
DBUS_ATTR_HWADDRESS,
|
DBUS_ATTR_HWADDRESS,
|
||||||
DBUS_ATTR_MANAGED,
|
DBUS_ATTR_MANAGED,
|
||||||
|
DBUS_ATTR_PATH,
|
||||||
DBUS_IFACE_DEVICE,
|
DBUS_IFACE_DEVICE,
|
||||||
DBUS_NAME_NM,
|
DBUS_NAME_NM,
|
||||||
DBUS_OBJECT_BASE,
|
DBUS_OBJECT_BASE,
|
||||||
@ -74,6 +75,12 @@ class NetworkInterface(DBusInterfaceProxy):
|
|||||||
"""Return hardware address (i.e. mac address) of device."""
|
"""Return hardware address (i.e. mac address) of device."""
|
||||||
return self.properties[DBUS_ATTR_HWADDRESS]
|
return self.properties[DBUS_ATTR_HWADDRESS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def path(self) -> str:
|
||||||
|
"""Return The path of the device as exposed by the udev property ID_PATH."""
|
||||||
|
return self.properties[DBUS_ATTR_PATH]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connection(self) -> NetworkConnection | None:
|
def connection(self) -> NetworkConnection | None:
|
||||||
"""Return the connection used for this interface."""
|
"""Return the connection used for this interface."""
|
||||||
|
@ -11,9 +11,9 @@ from ...interface import DBusInterface
|
|||||||
from ...utils import dbus_connected
|
from ...utils import dbus_connected
|
||||||
from ..configuration import (
|
from ..configuration import (
|
||||||
ConnectionProperties,
|
ConnectionProperties,
|
||||||
DeviceProperties,
|
|
||||||
EthernetProperties,
|
EthernetProperties,
|
||||||
IpProperties,
|
IpProperties,
|
||||||
|
MatchProperties,
|
||||||
VlanProperties,
|
VlanProperties,
|
||||||
WirelessProperties,
|
WirelessProperties,
|
||||||
WirelessSecurityProperties,
|
WirelessSecurityProperties,
|
||||||
@ -26,7 +26,8 @@ CONF_ATTR_802_WIRELESS_SECURITY = "802-11-wireless-security"
|
|||||||
CONF_ATTR_VLAN = "vlan"
|
CONF_ATTR_VLAN = "vlan"
|
||||||
CONF_ATTR_IPV4 = "ipv4"
|
CONF_ATTR_IPV4 = "ipv4"
|
||||||
CONF_ATTR_IPV6 = "ipv6"
|
CONF_ATTR_IPV6 = "ipv6"
|
||||||
CONF_ATTR_DEVICE = "device"
|
CONF_ATTR_MATCH = "match"
|
||||||
|
CONF_ATTR_PATH = "path"
|
||||||
|
|
||||||
ATTR_ID = "id"
|
ATTR_ID = "id"
|
||||||
ATTR_UUID = "uuid"
|
ATTR_UUID = "uuid"
|
||||||
@ -37,7 +38,7 @@ ATTR_POWERSAVE = "powersave"
|
|||||||
ATTR_AUTH_ALG = "auth-alg"
|
ATTR_AUTH_ALG = "auth-alg"
|
||||||
ATTR_KEY_MGMT = "key-mgmt"
|
ATTR_KEY_MGMT = "key-mgmt"
|
||||||
ATTR_INTERFACE_NAME = "interface-name"
|
ATTR_INTERFACE_NAME = "interface-name"
|
||||||
ATTR_MATCH_DEVICE = "match-device"
|
ATTR_PATH = "path"
|
||||||
|
|
||||||
IPV4_6_IGNORE_FIELDS = [
|
IPV4_6_IGNORE_FIELDS = [
|
||||||
"addresses",
|
"addresses",
|
||||||
@ -88,7 +89,7 @@ class NetworkSetting(DBusInterface):
|
|||||||
self._vlan: VlanProperties | None = None
|
self._vlan: VlanProperties | None = None
|
||||||
self._ipv4: IpProperties | None = None
|
self._ipv4: IpProperties | None = None
|
||||||
self._ipv6: IpProperties | None = None
|
self._ipv6: IpProperties | None = None
|
||||||
self._device: DeviceProperties | None = None
|
self._match: MatchProperties | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connection(self) -> ConnectionProperties | None:
|
def connection(self) -> ConnectionProperties | None:
|
||||||
@ -126,9 +127,9 @@ class NetworkSetting(DBusInterface):
|
|||||||
return self._ipv6
|
return self._ipv6
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device(self) -> DeviceProperties | None:
|
def match(self) -> MatchProperties | None:
|
||||||
"""Return device properties if any."""
|
"""Return match properties if any."""
|
||||||
return self._device
|
return self._match
|
||||||
|
|
||||||
@dbus_connected
|
@dbus_connected
|
||||||
async def get_settings(self) -> dict[str, Any]:
|
async def get_settings(self) -> dict[str, Any]:
|
||||||
@ -166,7 +167,7 @@ class NetworkSetting(DBusInterface):
|
|||||||
CONF_ATTR_IPV6,
|
CONF_ATTR_IPV6,
|
||||||
ignore_current_value=IPV4_6_IGNORE_FIELDS,
|
ignore_current_value=IPV4_6_IGNORE_FIELDS,
|
||||||
)
|
)
|
||||||
_merge_settings_attribute(new_settings, settings, CONF_ATTR_DEVICE)
|
_merge_settings_attribute(new_settings, settings, CONF_ATTR_MATCH)
|
||||||
|
|
||||||
await self.dbus.Settings.Connection.call_update(new_settings)
|
await self.dbus.Settings.Connection.call_update(new_settings)
|
||||||
|
|
||||||
@ -233,7 +234,5 @@ class NetworkSetting(DBusInterface):
|
|||||||
data[CONF_ATTR_IPV6].get(ATTR_METHOD),
|
data[CONF_ATTR_IPV6].get(ATTR_METHOD),
|
||||||
)
|
)
|
||||||
|
|
||||||
if CONF_ATTR_DEVICE in data:
|
if CONF_ATTR_MATCH in data:
|
||||||
self._device = DeviceProperties(
|
self._match = MatchProperties(data[CONF_ATTR_MATCH].get(ATTR_PATH))
|
||||||
data[CONF_ATTR_DEVICE].get(ATTR_MATCH_DEVICE)
|
|
||||||
)
|
|
||||||
|
@ -9,14 +9,14 @@ from dbus_fast import Variant
|
|||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTR_ASSIGNED_MAC,
|
ATTR_ASSIGNED_MAC,
|
||||||
ATTR_MATCH_DEVICE,
|
|
||||||
CONF_ATTR_802_ETHERNET,
|
CONF_ATTR_802_ETHERNET,
|
||||||
CONF_ATTR_802_WIRELESS,
|
CONF_ATTR_802_WIRELESS,
|
||||||
CONF_ATTR_802_WIRELESS_SECURITY,
|
CONF_ATTR_802_WIRELESS_SECURITY,
|
||||||
CONF_ATTR_CONNECTION,
|
CONF_ATTR_CONNECTION,
|
||||||
CONF_ATTR_DEVICE,
|
|
||||||
CONF_ATTR_IPV4,
|
CONF_ATTR_IPV4,
|
||||||
CONF_ATTR_IPV6,
|
CONF_ATTR_IPV6,
|
||||||
|
CONF_ATTR_MATCH,
|
||||||
|
CONF_ATTR_PATH,
|
||||||
CONF_ATTR_VLAN,
|
CONF_ATTR_VLAN,
|
||||||
)
|
)
|
||||||
from ....host.const import InterfaceMethod, InterfaceType
|
from ....host.const import InterfaceMethod, InterfaceType
|
||||||
@ -59,11 +59,7 @@ def get_connection_from_interface(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if interface.type != InterfaceType.VLAN:
|
if interface.type != InterfaceType.VLAN:
|
||||||
conn[CONF_ATTR_DEVICE] = {
|
conn[CONF_ATTR_MATCH] = {CONF_ATTR_PATH: Variant("as", [interface.path])}
|
||||||
ATTR_MATCH_DEVICE: Variant(
|
|
||||||
"s", f"mac:{interface.mac},interface-name:{interface.name}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4 = {}
|
ipv4 = {}
|
||||||
if not interface.ipv4 or interface.ipv4.method == InterfaceMethod.AUTO:
|
if not interface.ipv4 or interface.ipv4.method == InterfaceMethod.AUTO:
|
||||||
|
@ -61,6 +61,7 @@ class Interface:
|
|||||||
|
|
||||||
name: str
|
name: str
|
||||||
mac: str
|
mac: str
|
||||||
|
path: str
|
||||||
enabled: bool
|
enabled: bool
|
||||||
connected: bool
|
connected: bool
|
||||||
primary: bool
|
primary: bool
|
||||||
@ -75,12 +76,8 @@ class Interface:
|
|||||||
if not inet.settings:
|
if not inet.settings:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if inet.settings.device and inet.settings.device.match_device:
|
if inet.settings.match and inet.settings.match.path:
|
||||||
matchers = inet.settings.device.match_device.split(",", 1)
|
return inet.settings.match.path == [self.path]
|
||||||
return (
|
|
||||||
f"mac:{self.mac}" in matchers
|
|
||||||
or f"interface-name:{self.name}" in matchers
|
|
||||||
)
|
|
||||||
|
|
||||||
return inet.settings.connection.interface_name == self.name
|
return inet.settings.connection.interface_name == self.name
|
||||||
|
|
||||||
@ -108,6 +105,7 @@ class Interface:
|
|||||||
return Interface(
|
return Interface(
|
||||||
inet.name,
|
inet.name,
|
||||||
inet.hw_address,
|
inet.hw_address,
|
||||||
|
inet.path,
|
||||||
inet.settings is not None,
|
inet.settings is not None,
|
||||||
Interface._map_nm_connected(inet.connection),
|
Interface._map_nm_connected(inet.connection),
|
||||||
inet.primary,
|
inet.primary,
|
||||||
|
@ -17,10 +17,7 @@ async def test_get_connection_from_interface(network_manager: NetworkManager):
|
|||||||
|
|
||||||
assert "interface-name" not in connection_payload["connection"]
|
assert "interface-name" not in connection_payload["connection"]
|
||||||
assert connection_payload["connection"]["type"].value == "802-3-ethernet"
|
assert connection_payload["connection"]["type"].value == "802-3-ethernet"
|
||||||
assert (
|
assert connection_payload["match"]["path"].value == ["platform-ff3f0000.ethernet"]
|
||||||
connection_payload["device"]["match-device"].value
|
|
||||||
== "mac:AA:BB:CC:DD:EE:FF,interface-name:eth0"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert connection_payload["ipv4"]["method"].value == "auto"
|
assert connection_payload["ipv4"]["method"].value == "auto"
|
||||||
assert "address-data" not in connection_payload["ipv4"]
|
assert "address-data" not in connection_payload["ipv4"]
|
||||||
|
@ -58,9 +58,7 @@ async def test_update(
|
|||||||
)
|
)
|
||||||
assert settings["connection"]["autoconnect"] == Variant("b", True)
|
assert settings["connection"]["autoconnect"] == Variant("b", True)
|
||||||
|
|
||||||
assert settings["device"] == {
|
assert settings["match"] == {"path": Variant("as", ["platform-ff3f0000.ethernet"])}
|
||||||
"match-device": Variant("s", "mac:AA:BB:CC:DD:EE:FF,interface-name:eth0")
|
|
||||||
}
|
|
||||||
|
|
||||||
assert "ipv4" in settings
|
assert "ipv4" in settings
|
||||||
assert settings["ipv4"]["method"] == Variant("s", "auto")
|
assert settings["ipv4"]["method"] == Variant("s", "auto")
|
||||||
|
@ -77,9 +77,7 @@ SETINGS_FIXTURES: dict[str, dict[str, dict[str, Variant]]] = {
|
|||||||
"proxy": {},
|
"proxy": {},
|
||||||
"802-3-ethernet": SETTINGS_FIXTURE["802-3-ethernet"],
|
"802-3-ethernet": SETTINGS_FIXTURE["802-3-ethernet"],
|
||||||
"802-11-wireless": SETTINGS_FIXTURE["802-11-wireless"],
|
"802-11-wireless": SETTINGS_FIXTURE["802-11-wireless"],
|
||||||
"device": {
|
"match": {"path": Variant("as", ["platform-ff3f0000.ethernet"])},
|
||||||
"match-device": Variant("s", "mac:AA:BB:CC:DD:EE:FF,interface-name:eth0"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ from supervisor.exceptions import HostNotSupportedError
|
|||||||
from supervisor.homeassistant.const import WSEvent, WSType
|
from supervisor.homeassistant.const import WSEvent, WSType
|
||||||
from supervisor.host.const import WifiMode
|
from supervisor.host.const import WifiMode
|
||||||
|
|
||||||
from tests.common import mock_dbus_services
|
|
||||||
from tests.dbus_service_mocks.base import DBusServiceMock
|
from tests.dbus_service_mocks.base import DBusServiceMock
|
||||||
from tests.dbus_service_mocks.network_active_connection import (
|
from tests.dbus_service_mocks.network_active_connection import (
|
||||||
ActiveConnection as ActiveConnectionService,
|
ActiveConnection as ActiveConnectionService,
|
||||||
@ -253,47 +252,3 @@ async def test_host_connectivity_disabled(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
assert "connectivity_check" not in coresys.resolution.unsupported
|
assert "connectivity_check" not in coresys.resolution.unsupported
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"interface_obj_path",
|
|
||||||
[
|
|
||||||
"/org/freedesktop/NetworkManager/Devices/4",
|
|
||||||
"/org/freedesktop/NetworkManager/Devices/5",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_load_with_mac_or_name_change(
|
|
||||||
coresys: CoreSys,
|
|
||||||
network_manager_service: NetworkManagerService,
|
|
||||||
interface_obj_path: str,
|
|
||||||
):
|
|
||||||
"""Test load fixes match-device settings if mac address or interface name has changed."""
|
|
||||||
await mock_dbus_services(
|
|
||||||
{
|
|
||||||
"network_active_connection": "/org/freedesktop/NetworkManager/ActiveConnection/2",
|
|
||||||
"network_connection_settings": "/org/freedesktop/NetworkManager/Settings/2",
|
|
||||||
"network_device": interface_obj_path,
|
|
||||||
},
|
|
||||||
coresys.dbus.bus,
|
|
||||||
)
|
|
||||||
await coresys.dbus.network.update({"Devices": [interface_obj_path]})
|
|
||||||
|
|
||||||
network_manager_service.ActivateConnection.calls.clear()
|
|
||||||
assert len(coresys.dbus.network.interfaces) == 1
|
|
||||||
interface = next(iter(coresys.dbus.network.interfaces))
|
|
||||||
assert interface.object_path == interface_obj_path
|
|
||||||
expected_match_device = (
|
|
||||||
f"mac:{interface.hw_address},interface-name:{interface.name}"
|
|
||||||
)
|
|
||||||
assert interface.settings.device.match_device != expected_match_device
|
|
||||||
|
|
||||||
await coresys.host.network.load()
|
|
||||||
|
|
||||||
assert network_manager_service.ActivateConnection.calls == [
|
|
||||||
(
|
|
||||||
"/org/freedesktop/NetworkManager/Settings/2",
|
|
||||||
interface_obj_path,
|
|
||||||
"/",
|
|
||||||
)
|
|
||||||
]
|
|
||||||
assert interface.settings.device.match_device == expected_match_device
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user