mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-11-10 03:19:37 +00:00
* Fix NetworkManager connection name for VLANs The connection name for VLANs should include the parent interface name for better identification. This was originally the intention, but the interface object's name property was used which appears empty at that point. * Disallow creating multiple connections for the same VLAN id Only allow a single connection per interface and VLAN id. The regular network commands can be used to alter the configuration. * Fix pytest * Simply connection id name generation Always rely on the Supervisor interface representation's name attribute to generate the NetworkManager connection id. Make sure that the name is correctly set when creating VLAN interfaces as well. * Special case VLAN configuration We can't use the match information when comparing Supervisor interface representation with D-Bus representations. Special case VLAN and compare using VLAN ID and parent interface. Note that this currently compares connection UUID of the parent interface. * Fix pytest * Separate VLAN creation logic from apply_changes Apply changes is really all about updating the NetworkManager settings of a particular network interface. The base in apply_changes() is NetworkInterface class, which is the NetworkManager Device abstraction. All physical interfaces have such a Device hence it is always present. The only exception is when creating a VLAN: Since it is a virtual device, there is no device when creating a VLAN. This separate the two cases. This makes it much easier to reason if a VLAN already exists or not, and to handle the case where a VLAN needs to be created. For all other network interfaces, the apply_changes() method can now rely on the presence of the NetworkInterface Device abstraction. * Add VLAN test interface and VLAN exists test Add a test which checks that an error gets raised when a VLAN for a particular interface/id combination already exists. * Address pylint * Fix test_ignore_veth_only_changes pytest * Make VLAN interface disabled to avoid test issues * Reference setting 38 in mocked connection * Make sure interface type matches Require a interface type match before doing any comparision. * Add Supervisor host network configuration tests * Fix device type checking * Fix pytest * Fix tests by taking VLAN interface into account * Fix test_load_with_network_connection_issues This seems like a hack, but it turns out that the additional active connection caused coresys.host.network.update() to be called, which implicitly "fake" activated the connection. Now it seems that our mocking causes IPv4 gateway to be set. So in a way, the test checked a particular mock behavior instead of actual intention. The crucial part of this test is that we make sure the settings remain unchanged. This is done by ensuring that the the method is still auto. * Fix test_check_network_interface_ipv4.py Now that we have the VLAN interface active too it will raise an issue as well. * Apply suggestions from code review Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Fix ruff check issue --------- Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
478 lines
14 KiB
Python
478 lines
14 KiB
Python
"""Mock of Network Manager Device service."""
|
|
|
|
from ctypes import c_uint32
|
|
from dataclasses import dataclass
|
|
|
|
from dbus_fast import Variant
|
|
from dbus_fast.service import PropertyAccess, dbus_property, signal
|
|
|
|
from .base import DBusServiceMock, dbus_method
|
|
|
|
BUS_NAME = "org.freedesktop.NetworkManager"
|
|
ETHERNET_DEVICE_OBJECT_PATH = "/org/freedesktop/NetworkManager/Devices/1"
|
|
WIRELESS_DEVICE_OBJECT_PATH = "/org/freedesktop/NetworkManager/Devices/3"
|
|
DEFAULT_OBJECT_PATH = ETHERNET_DEVICE_OBJECT_PATH
|
|
|
|
|
|
def setup(object_path: str | None = None) -> DBusServiceMock:
|
|
"""Create dbus mock object."""
|
|
return Device(object_path if object_path else DEFAULT_OBJECT_PATH)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class DeviceFixture:
|
|
"""Device fixture."""
|
|
|
|
Udi: str
|
|
Path: str
|
|
Interface: str
|
|
IpInterface: str
|
|
Driver: str
|
|
DriverVersion: str
|
|
FirmwareVersion: str
|
|
Capabilities: c_uint32
|
|
Ip4Address: c_uint32
|
|
State: c_uint32
|
|
StateReason: list[c_uint32]
|
|
ActiveConnection: str
|
|
Ip4Config: str
|
|
Dhcp4Config: str
|
|
Ip6Config: str
|
|
Dhcp6Config: str
|
|
Managed: bool
|
|
Autoconnect: bool
|
|
FirmwareMissing: bool
|
|
NmPluginMissing: bool
|
|
DeviceType: c_uint32
|
|
AvailableConnections: list[str]
|
|
PhysicalPortId: str
|
|
Mtu: c_uint32
|
|
Metered: c_uint32
|
|
LldpNeighbors: list[dict[str, Variant]]
|
|
Real: bool
|
|
Ip4Connectivity: c_uint32
|
|
Ip6Connectivity: c_uint32
|
|
InterfaceFlags: c_uint32
|
|
HwAddress: str
|
|
Ports: list[str]
|
|
# VLAN specific properties
|
|
VlanId: c_uint32 | None = None
|
|
Parent: str | None = None
|
|
|
|
|
|
FIXTURES: dict[str, DeviceFixture] = {
|
|
"/org/freedesktop/NetworkManager/Devices/1": DeviceFixture(
|
|
Udi="/sys/devices/pci0000:00/0000:00:1f.6/net/eth0",
|
|
Path="platform-ff3f0000.ethernet",
|
|
Interface="eth0",
|
|
IpInterface="eth0",
|
|
Driver="e1000e",
|
|
DriverVersion="3.2.6-k",
|
|
FirmwareVersion="0.7-4",
|
|
Capabilities=3,
|
|
Ip4Address=2499979456,
|
|
State=100,
|
|
StateReason=[100, 0],
|
|
ActiveConnection="/org/freedesktop/NetworkManager/ActiveConnection/1",
|
|
Ip4Config="/org/freedesktop/NetworkManager/IP4Config/1",
|
|
Dhcp4Config="/org/freedesktop/NetworkManager/DHCP4Config/1",
|
|
Ip6Config="/org/freedesktop/NetworkManager/IP6Config/1",
|
|
Dhcp6Config="/",
|
|
Managed=True,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=1,
|
|
AvailableConnections=["/org/freedesktop/NetworkManager/Settings/1"],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=4,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=4,
|
|
Ip6Connectivity=3,
|
|
InterfaceFlags=65539,
|
|
HwAddress="AA:BB:CC:DD:EE:FF",
|
|
Ports=[],
|
|
),
|
|
"/org/freedesktop/NetworkManager/Devices/3": DeviceFixture(
|
|
Udi="/sys/devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0",
|
|
Path="platform.fe300000.mmcnr",
|
|
Interface="wlan0",
|
|
IpInterface="",
|
|
Driver="brcmfmac",
|
|
DriverVersion="7.45.154",
|
|
FirmwareVersion="01-4fbe0b04",
|
|
Capabilities=1,
|
|
Ip4Address=0,
|
|
State=30,
|
|
StateReason=[30, 42],
|
|
ActiveConnection="/",
|
|
Ip4Config="/",
|
|
Dhcp4Config="/",
|
|
Ip6Config="/",
|
|
Dhcp6Config="/",
|
|
Managed=True,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=2,
|
|
AvailableConnections=["/org/freedesktop/NetworkManager/Settings/3"],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=0,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=1,
|
|
Ip6Connectivity=1,
|
|
InterfaceFlags=65539,
|
|
HwAddress="FF:EE:DD:CC:BB:AA",
|
|
Ports=[],
|
|
),
|
|
"/org/freedesktop/NetworkManager/Devices/4": DeviceFixture(
|
|
Udi="/sys/devices/pci0000:00/0000:00:1f.6/net/eth0",
|
|
Path="platform-ff3f0000.ethernet",
|
|
Interface="eth0",
|
|
IpInterface="eth0",
|
|
Driver="e1000e",
|
|
DriverVersion="3.2.6-k",
|
|
FirmwareVersion="0.7-4",
|
|
Capabilities=3,
|
|
Ip4Address=2499979456,
|
|
State=100,
|
|
StateReason=[100, 0],
|
|
ActiveConnection="/org/freedesktop/NetworkManager/ActiveConnection/2",
|
|
Ip4Config="/org/freedesktop/NetworkManager/IP4Config/1",
|
|
Dhcp4Config="/org/freedesktop/NetworkManager/DHCP4Config/1",
|
|
Ip6Config="/org/freedesktop/NetworkManager/IP6Config/1",
|
|
Dhcp6Config="/",
|
|
Managed=True,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=1,
|
|
AvailableConnections=["/org/freedesktop/NetworkManager/Settings/2"],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=4,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=4,
|
|
Ip6Connectivity=3,
|
|
InterfaceFlags=65539,
|
|
HwAddress="A1:B2:C3:D4:E5:F6",
|
|
Ports=[],
|
|
),
|
|
"/org/freedesktop/NetworkManager/Devices/5": DeviceFixture(
|
|
Udi="/sys/devices/pci0000:00/0000:00:1f.6/net/en0",
|
|
Path="platform-ff3f0000.ethernet",
|
|
Interface="en0",
|
|
IpInterface="en0",
|
|
Driver="e1000e",
|
|
DriverVersion="3.2.6-k",
|
|
FirmwareVersion="0.7-4",
|
|
Capabilities=3,
|
|
Ip4Address=2499979456,
|
|
State=100,
|
|
StateReason=[100, 0],
|
|
ActiveConnection="/org/freedesktop/NetworkManager/ActiveConnection/2",
|
|
Ip4Config="/org/freedesktop/NetworkManager/IP4Config/1",
|
|
Dhcp4Config="/org/freedesktop/NetworkManager/DHCP4Config/1",
|
|
Ip6Config="/org/freedesktop/NetworkManager/IP6Config/1",
|
|
Dhcp6Config="/",
|
|
Managed=True,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=1,
|
|
AvailableConnections=["/org/freedesktop/NetworkManager/Settings/2"],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=4,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=4,
|
|
Ip6Connectivity=3,
|
|
InterfaceFlags=65539,
|
|
HwAddress="AA:BB:CC:DD:EE:FF",
|
|
Ports=[],
|
|
),
|
|
"/org/freedesktop/NetworkManager/Devices/35": DeviceFixture(
|
|
Udi="/sys/devices/virtual/net/veth87bd238'",
|
|
Path="",
|
|
Interface="veth87bd238",
|
|
IpInterface="veth87bd238",
|
|
Driver="veth",
|
|
DriverVersion="1.0",
|
|
FirmwareVersion="",
|
|
Capabilities=7,
|
|
Ip4Address=0,
|
|
State=10,
|
|
StateReason=[10, 0],
|
|
ActiveConnection="/",
|
|
Ip4Config="/",
|
|
Dhcp4Config="/",
|
|
Ip6Config="/",
|
|
Dhcp6Config="/",
|
|
Managed=False,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=20,
|
|
AvailableConnections=[],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=0,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=0,
|
|
Ip6Connectivity=0,
|
|
InterfaceFlags=65539,
|
|
HwAddress="9A:4B:E3:9A:F8:D3",
|
|
Ports=[],
|
|
),
|
|
"/org/freedesktop/NetworkManager/Devices/38": DeviceFixture(
|
|
Udi="/sys/devices/virtual/net/eth0.10",
|
|
Path="",
|
|
Interface="eth0.10",
|
|
IpInterface="eth0.10",
|
|
Driver="vlan",
|
|
DriverVersion="1.8",
|
|
FirmwareVersion="N/A",
|
|
Capabilities=7,
|
|
Ip4Address=0,
|
|
State=100,
|
|
StateReason=[100, 0],
|
|
ActiveConnection="/org/freedesktop/NetworkManager/ActiveConnection/38",
|
|
Ip4Config="/org/freedesktop/NetworkManager/IP4Config/1",
|
|
Dhcp4Config="/org/freedesktop/NetworkManager/DHCP4Config/1",
|
|
Ip6Config="/org/freedesktop/NetworkManager/IP6Config/1",
|
|
Dhcp6Config="/",
|
|
Managed=True,
|
|
Autoconnect=True,
|
|
FirmwareMissing=False,
|
|
NmPluginMissing=False,
|
|
DeviceType=11,
|
|
AvailableConnections=["/org/freedesktop/NetworkManager/Settings/38"],
|
|
PhysicalPortId="",
|
|
Mtu=1500,
|
|
Metered=4,
|
|
LldpNeighbors=[],
|
|
Real=True,
|
|
Ip4Connectivity=1,
|
|
Ip6Connectivity=3,
|
|
InterfaceFlags=65539,
|
|
HwAddress="52:54:00:2B:36:80",
|
|
Ports=[],
|
|
VlanId=10,
|
|
Parent="/org/freedesktop/NetworkManager/Devices/1",
|
|
),
|
|
}
|
|
|
|
|
|
class Device(DBusServiceMock):
|
|
"""Device mock.
|
|
|
|
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/Devices/1
|
|
"""
|
|
|
|
interface = "org.freedesktop.NetworkManager.Device"
|
|
|
|
def __init__(self, object_path: str):
|
|
"""Initialize object."""
|
|
super().__init__()
|
|
self.object_path = object_path
|
|
self.fixture: DeviceFixture = FIXTURES[object_path]
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Udi(self) -> "s":
|
|
"""Get Udi."""
|
|
return self.fixture.Udi
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Path(self) -> "s":
|
|
"""Get Path."""
|
|
return self.fixture.Path
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Interface(self) -> "s":
|
|
"""Get Interface."""
|
|
return self.fixture.Interface
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def IpInterface(self) -> "s":
|
|
"""Get IpInterface."""
|
|
return self.fixture.IpInterface
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Driver(self) -> "s":
|
|
"""Get Driver."""
|
|
return self.fixture.Driver
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def DriverVersion(self) -> "s":
|
|
"""Get DriverVersion."""
|
|
return self.fixture.DriverVersion
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def FirmwareVersion(self) -> "s":
|
|
"""Get FirmwareVersion."""
|
|
return self.fixture.FirmwareVersion
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Capabilities(self) -> "u":
|
|
"""Get Capabilities."""
|
|
return self.fixture.Capabilities
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ip4Address(self) -> "u":
|
|
"""Get Ip4Address."""
|
|
return self.fixture.Ip4Address
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def State(self) -> "u":
|
|
"""Get State."""
|
|
return self.fixture.State
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def StateReason(self) -> "(uu)":
|
|
"""Get StateReason."""
|
|
return self.fixture.StateReason
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def ActiveConnection(self) -> "o":
|
|
"""Get ActiveConnection."""
|
|
return self.fixture.ActiveConnection
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ip4Config(self) -> "o":
|
|
"""Get Ip4Config."""
|
|
return self.fixture.Ip4Config
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Dhcp4Config(self) -> "o":
|
|
"""Get Dhcp4Config."""
|
|
return self.fixture.Dhcp4Config
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ip6Config(self) -> "o":
|
|
"""Get Ip6Config."""
|
|
return self.fixture.Ip6Config
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Dhcp6Config(self) -> "o":
|
|
"""Get Dhcp6Config."""
|
|
return self.fixture.Dhcp6Config
|
|
|
|
@dbus_property()
|
|
def Managed(self) -> "b":
|
|
"""Get Managed."""
|
|
return self.fixture.Managed
|
|
|
|
@Managed.setter
|
|
def Managed(self, value: "b"):
|
|
"""Set Managed."""
|
|
self.emit_properties_changed({"Managed": value})
|
|
|
|
@dbus_property()
|
|
def Autoconnect(self) -> "b":
|
|
"""Get Autoconnect."""
|
|
return self.fixture.Autoconnect
|
|
|
|
@Autoconnect.setter
|
|
def Autoconnect(self, value: "b"):
|
|
"""Set Autoconnect."""
|
|
self.emit_properties_changed({"Autoconnect": value})
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def FirmwareMissing(self) -> "b":
|
|
"""Get FirmwareMissing."""
|
|
return self.fixture.FirmwareMissing
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def NmPluginMissing(self) -> "b":
|
|
"""Get NmPluginMissing."""
|
|
return self.fixture.NmPluginMissing
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def DeviceType(self) -> "u":
|
|
"""Get DeviceType."""
|
|
return self.fixture.DeviceType
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def AvailableConnections(self) -> "ao":
|
|
"""Get AvailableConnections."""
|
|
return self.fixture.AvailableConnections
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def PhysicalPortId(self) -> "s":
|
|
"""Get PhysicalPortId."""
|
|
return self.fixture.PhysicalPortId
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Mtu(self) -> "u":
|
|
"""Get Mtu."""
|
|
return self.fixture.Mtu
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Metered(self) -> "u":
|
|
"""Get Metered."""
|
|
return self.fixture.Metered
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def LldpNeighbors(self) -> "aa{sv}":
|
|
"""Get LldpNeighbors."""
|
|
return self.fixture.LldpNeighbors
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Real(self) -> "b":
|
|
"""Get Real."""
|
|
return self.fixture.Real
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ip4Connectivity(self) -> "u":
|
|
"""Get Ip4Connectivity."""
|
|
return self.fixture.Ip4Connectivity
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ip6Connectivity(self) -> "u":
|
|
"""Get Ip6Connectivity."""
|
|
return self.fixture.Ip6Connectivity
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def InterfaceFlags(self) -> "u":
|
|
"""Get InterfaceFlags."""
|
|
return self.fixture.InterfaceFlags
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def HwAddress(self) -> "s":
|
|
"""Get HwAddress."""
|
|
return self.fixture.HwAddress
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def Ports(self) -> "ao":
|
|
"""Get Ports."""
|
|
return self.fixture.Ports
|
|
|
|
@signal()
|
|
def StateChanged(self) -> "uuu":
|
|
"""Signal StateChanged."""
|
|
return [120, 100, 1]
|
|
|
|
@dbus_method()
|
|
def Reapply(self, connection: "a{sa{sv}}", version_id: "t", flags: "u") -> None:
|
|
"""Do Reapply method."""
|
|
|
|
@dbus_method()
|
|
def GetAppliedConnection(self, flags: "u") -> "a{sa{sv}}t":
|
|
"""Do GetAppliedConnection method."""
|
|
return [{}, 0]
|
|
|
|
@dbus_method()
|
|
def Disconnect(self) -> None:
|
|
"""Do Disconnect method."""
|
|
|
|
@dbus_method()
|
|
def Delete(self) -> None:
|
|
"""Do Delete method."""
|