mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-14 20:56:30 +00:00
Split payload so we can set auto without IP configuration (#1982)
* Guard for no interfaces * Host reload on update * Extract payload * Check eth and wifi interfaces with a valid ip4 config * Add tests * Fix tests * Move to enum
This commit is contained in:
parent
a2821a98ad
commit
8d3694884d
2
setup.py
2
setup.py
@ -36,6 +36,8 @@ setup(
|
|||||||
"supervisor.addons",
|
"supervisor.addons",
|
||||||
"supervisor.api",
|
"supervisor.api",
|
||||||
"supervisor.dbus",
|
"supervisor.dbus",
|
||||||
|
"supervisor.dbus.payloads",
|
||||||
|
"supervisor.dbus.network",
|
||||||
"supervisor.discovery",
|
"supervisor.discovery",
|
||||||
"supervisor.discovery.services",
|
"supervisor.discovery.services",
|
||||||
"supervisor.services",
|
"supervisor.services",
|
||||||
|
@ -93,6 +93,6 @@ class APINetwork(CoreSysAttributes):
|
|||||||
self.sys_host.network.interfaces[req_interface].update_settings(**args)
|
self.sys_host.network.interfaces[req_interface].update_settings(**args)
|
||||||
)
|
)
|
||||||
|
|
||||||
await asyncio.shield(self.sys_host.network.update())
|
await asyncio.shield(self.sys_host.reload())
|
||||||
|
|
||||||
return await asyncio.shield(self.interface_info(request))
|
return await asyncio.shield(self.interface_info(request))
|
||||||
|
@ -72,3 +72,10 @@ class InterfaceMethodSimple(str, Enum):
|
|||||||
|
|
||||||
DHCP = "dhcp"
|
DHCP = "dhcp"
|
||||||
STATIC = "static"
|
STATIC = "static"
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionType(str, Enum):
|
||||||
|
"""Connection type."""
|
||||||
|
|
||||||
|
ETHERNET = "802-3-ethernet"
|
||||||
|
WIRELESS = "802-11-wireless"
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from ...exceptions import DBusError, DBusInterfaceError
|
from ...exceptions import DBusError, DBusFatalError, DBusInterfaceError
|
||||||
from ...utils.gdbus import DBus
|
from ...utils.gdbus import DBus
|
||||||
from ..const import (
|
from ..const import (
|
||||||
DBUS_ATTR_ACTIVE_CONNECTIONS,
|
DBUS_ATTR_ACTIVE_CONNECTIONS,
|
||||||
DBUS_ATTR_PRIMARY_CONNECTION,
|
DBUS_ATTR_PRIMARY_CONNECTION,
|
||||||
DBUS_NAME_NM,
|
DBUS_NAME_NM,
|
||||||
DBUS_OBJECT_NM,
|
DBUS_OBJECT_NM,
|
||||||
|
ConnectionType,
|
||||||
)
|
)
|
||||||
from ..interface import DBusInterface
|
from ..interface import DBusInterface
|
||||||
from ..utils import dbus_connected
|
from ..utils import dbus_connected
|
||||||
@ -65,11 +66,17 @@ class NetworkManager(DBusInterface):
|
|||||||
|
|
||||||
await interface.connect(self.dbus, connection)
|
await interface.connect(self.dbus, connection)
|
||||||
|
|
||||||
if not interface.connection.default:
|
if interface.connection.type not in [
|
||||||
|
ConnectionType.ETHERNET,
|
||||||
|
ConnectionType.WIRELESS,
|
||||||
|
]:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
await interface.connection.update_information()
|
await interface.connection.update_information()
|
||||||
except IndexError:
|
except (IndexError, DBusFatalError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not interface.connection.ip4_config:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if interface.connection.object_path == data.get(
|
if interface.connection.object_path == data.get(
|
||||||
|
@ -22,6 +22,7 @@ from ..const import (
|
|||||||
DBUS_NAME_DEVICE,
|
DBUS_NAME_DEVICE,
|
||||||
DBUS_NAME_IP4CONFIG,
|
DBUS_NAME_IP4CONFIG,
|
||||||
DBUS_NAME_NM,
|
DBUS_NAME_NM,
|
||||||
|
DBUS_OBJECT_BASE,
|
||||||
)
|
)
|
||||||
from .configuration import (
|
from .configuration import (
|
||||||
AddressData,
|
AddressData,
|
||||||
@ -91,6 +92,9 @@ class NetworkConnection(NetworkAttributes):
|
|||||||
|
|
||||||
async def update_information(self):
|
async def update_information(self):
|
||||||
"""Update the information for childs ."""
|
"""Update the information for childs ."""
|
||||||
|
if self._properties[DBUS_ATTR_IP4CONFIG] == DBUS_OBJECT_BASE:
|
||||||
|
return
|
||||||
|
|
||||||
settings = await DBus.connect(
|
settings = await DBus.connect(
|
||||||
DBUS_NAME_NM, self._properties[DBUS_ATTR_CONNECTION]
|
DBUS_NAME_NM, self._properties[DBUS_ATTR_CONNECTION]
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""NetworkInterface object for Network Manager."""
|
"""NetworkInterface object for Network Manager."""
|
||||||
from ...const import ATTR_ADDRESS, ATTR_DNS, ATTR_GATEWAY, ATTR_METHOD, ATTR_PREFIX
|
|
||||||
from ...utils.gdbus import DBus
|
from ...utils.gdbus import DBus
|
||||||
from ..const import (
|
from ..const import (
|
||||||
DBUS_NAME_CONNECTION_ACTIVE,
|
DBUS_NAME_CONNECTION_ACTIVE,
|
||||||
@ -7,8 +6,8 @@ from ..const import (
|
|||||||
DBUS_OBJECT_BASE,
|
DBUS_OBJECT_BASE,
|
||||||
InterfaceMethod,
|
InterfaceMethod,
|
||||||
)
|
)
|
||||||
|
from ..payloads.interface_update import interface_update_payload
|
||||||
from .connection import NetworkConnection
|
from .connection import NetworkConnection
|
||||||
from .utils import ip2int
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkInterface:
|
class NetworkInterface:
|
||||||
@ -85,42 +84,9 @@ class NetworkInterface:
|
|||||||
|
|
||||||
async def update_settings(self, **kwargs) -> None:
|
async def update_settings(self, **kwargs) -> None:
|
||||||
"""Update IP configuration used for this interface."""
|
"""Update IP configuration used for this interface."""
|
||||||
if kwargs.get(ATTR_DNS):
|
payload = interface_update_payload(self, **kwargs)
|
||||||
kwargs[ATTR_DNS] = [ip2int(x.strip()) for x in kwargs[ATTR_DNS]]
|
|
||||||
|
|
||||||
if kwargs.get(ATTR_METHOD):
|
await self.connection.settings.dbus.Settings.Connection.Update(payload)
|
||||||
kwargs[ATTR_METHOD] = (
|
|
||||||
InterfaceMethod.MANUAL
|
|
||||||
if kwargs[ATTR_METHOD] == "static"
|
|
||||||
else InterfaceMethod.AUTO
|
|
||||||
)
|
|
||||||
|
|
||||||
if kwargs.get(ATTR_ADDRESS):
|
|
||||||
if "/" in kwargs[ATTR_ADDRESS]:
|
|
||||||
kwargs[ATTR_PREFIX] = kwargs[ATTR_ADDRESS].split("/")[-1]
|
|
||||||
kwargs[ATTR_ADDRESS] = kwargs[ATTR_ADDRESS].split("/")[0]
|
|
||||||
kwargs[ATTR_METHOD] = InterfaceMethod.MANUAL
|
|
||||||
|
|
||||||
await self.connection.settings.dbus.Settings.Connection.Update(
|
|
||||||
f"""{{
|
|
||||||
'connection':
|
|
||||||
{{
|
|
||||||
'id': <'{self.id}'>,
|
|
||||||
'type': <'{self.type}'>
|
|
||||||
}},
|
|
||||||
'ipv4':
|
|
||||||
{{
|
|
||||||
'method': <'{kwargs.get(ATTR_METHOD, self.method)}'>,
|
|
||||||
'dns': <[{",".join([f"uint32 {x}" for x in kwargs.get(ATTR_DNS, self.nameservers)])}]>,
|
|
||||||
'address-data': <[
|
|
||||||
{{
|
|
||||||
'address': <'{kwargs.get(ATTR_ADDRESS, self.ip_address)}'>,
|
|
||||||
'prefix': <uint32 {kwargs.get(ATTR_PREFIX, self.prefix)}>
|
|
||||||
}}]>,
|
|
||||||
'gateway': <'{kwargs.get(ATTR_GATEWAY, self.gateway)}'>
|
|
||||||
}}
|
|
||||||
}}"""
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.nm_dbus.ActivateConnection(
|
await self.nm_dbus.ActivateConnection(
|
||||||
self.connection.settings.dbus.object_path,
|
self.connection.settings.dbus.object_path,
|
||||||
|
1
supervisor/dbus/payloads/__init__.py
Normal file
1
supervisor/dbus/payloads/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Init file for DBUS payloads."""
|
55
supervisor/dbus/payloads/interface_update.py
Normal file
55
supervisor/dbus/payloads/interface_update.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""Payload generators for DBUS communication."""
|
||||||
|
from ...const import ATTR_ADDRESS, ATTR_DNS, ATTR_GATEWAY, ATTR_METHOD, ATTR_PREFIX
|
||||||
|
from ..const import InterfaceMethod
|
||||||
|
from ..network.utils import ip2int
|
||||||
|
|
||||||
|
|
||||||
|
def interface_update_payload(interface, **kwargs) -> str:
|
||||||
|
"""Generate a payload for network interface update."""
|
||||||
|
if kwargs.get(ATTR_DNS):
|
||||||
|
kwargs[ATTR_DNS] = [ip2int(x.strip()) for x in kwargs[ATTR_DNS]]
|
||||||
|
|
||||||
|
if kwargs.get(ATTR_METHOD):
|
||||||
|
kwargs[ATTR_METHOD] = (
|
||||||
|
InterfaceMethod.MANUAL
|
||||||
|
if kwargs[ATTR_METHOD] == "static"
|
||||||
|
else InterfaceMethod.AUTO
|
||||||
|
)
|
||||||
|
|
||||||
|
if kwargs.get(ATTR_ADDRESS):
|
||||||
|
if "/" in kwargs[ATTR_ADDRESS]:
|
||||||
|
kwargs[ATTR_PREFIX] = kwargs[ATTR_ADDRESS].split("/")[-1]
|
||||||
|
kwargs[ATTR_ADDRESS] = kwargs[ATTR_ADDRESS].split("/")[0]
|
||||||
|
kwargs[ATTR_METHOD] = InterfaceMethod.MANUAL
|
||||||
|
|
||||||
|
if kwargs.get(ATTR_METHOD) == "auto":
|
||||||
|
return f"""{{
|
||||||
|
'connection':
|
||||||
|
{{
|
||||||
|
'id': <'{interface.id}'>,
|
||||||
|
'type': <'{interface.type}'>
|
||||||
|
}},
|
||||||
|
'ipv4':
|
||||||
|
{{
|
||||||
|
'method': <'{InterfaceMethod.AUTO}'>
|
||||||
|
}}
|
||||||
|
}}"""
|
||||||
|
|
||||||
|
return f"""{{
|
||||||
|
'connection':
|
||||||
|
{{
|
||||||
|
'id': <'{interface.id}'>,
|
||||||
|
'type': <'{interface.type}'>
|
||||||
|
}},
|
||||||
|
'ipv4':
|
||||||
|
{{
|
||||||
|
'method': <'{InterfaceMethod.MANUAL}'>,
|
||||||
|
'dns': <[{",".join([f"uint32 {x}" for x in kwargs.get(ATTR_DNS, interface.nameservers)])}]>,
|
||||||
|
'address-data': <[
|
||||||
|
{{
|
||||||
|
'address': <'{kwargs.get(ATTR_ADDRESS, interface.ip_address)}'>,
|
||||||
|
'prefix': <uint32 {kwargs.get(ATTR_PREFIX, interface.prefix)}>
|
||||||
|
}}]>,
|
||||||
|
'gateway': <'{kwargs.get(ATTR_GATEWAY, interface.gateway)}'>
|
||||||
|
}}
|
||||||
|
}}"""
|
@ -69,7 +69,7 @@ class HostManager(CoreSysAttributes):
|
|||||||
[HostFeature.REBOOT, HostFeature.SHUTDOWN, HostFeature.SERVICES]
|
[HostFeature.REBOOT, HostFeature.SHUTDOWN, HostFeature.SERVICES]
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.sys_dbus.network.is_connected:
|
if self.sys_dbus.network.is_connected and self.sys_dbus.network.interfaces:
|
||||||
features.append(HostFeature.NETWORK)
|
features.append(HostFeature.NETWORK)
|
||||||
|
|
||||||
if self.sys_dbus.hostname.is_connected:
|
if self.sys_dbus.hostname.is_connected:
|
||||||
|
@ -11,6 +11,7 @@ from supervisor.bootstrap import initialize_coresys
|
|||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
from supervisor.dbus.const import DBUS_NAME_NM, DBUS_OBJECT_BASE
|
from supervisor.dbus.const import DBUS_NAME_NM, DBUS_OBJECT_BASE
|
||||||
from supervisor.dbus.network import NetworkManager
|
from supervisor.dbus.network import NetworkManager
|
||||||
|
from supervisor.dbus.network.interface import NetworkInterface
|
||||||
from supervisor.docker import DockerAPI
|
from supervisor.docker import DockerAPI
|
||||||
from supervisor.utils.gdbus import DBus
|
from supervisor.utils.gdbus import DBus
|
||||||
|
|
||||||
@ -124,3 +125,12 @@ async def api_client(aiohttp_client, coresys):
|
|||||||
api.webapp = web.Application()
|
api.webapp = web.Application()
|
||||||
await api.load()
|
await api.load()
|
||||||
yield await aiohttp_client(api.webapp)
|
yield await aiohttp_client(api.webapp)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def network_interface(dbus):
|
||||||
|
"""Fixture for a network interface."""
|
||||||
|
interface = NetworkInterface()
|
||||||
|
await interface.connect(dbus, "/org/freedesktop/NetworkManager/ActiveConnection/1")
|
||||||
|
await interface.connection.update_information()
|
||||||
|
yield interface
|
||||||
|
49
tests/dbus/payloads/test_interface_update_payload.py
Normal file
49
tests/dbus/payloads/test_interface_update_payload.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"""Test interface update payload."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from supervisor.dbus.payloads.interface_update import interface_update_payload
|
||||||
|
from supervisor.utils.gdbus import DBus
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_interface_update_payload(network_interface):
|
||||||
|
"""Test interface update payload."""
|
||||||
|
assert (
|
||||||
|
interface_update_payload(network_interface, **{"method": "auto"})
|
||||||
|
== """{
|
||||||
|
'connection':
|
||||||
|
{
|
||||||
|
'id': <'Wired connection 1'>,
|
||||||
|
'type': <'802-3-ethernet'>
|
||||||
|
},
|
||||||
|
'ipv4':
|
||||||
|
{
|
||||||
|
'method': <'auto'>
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
interface_update_payload(network_interface, **{})
|
||||||
|
== """{
|
||||||
|
'connection':
|
||||||
|
{
|
||||||
|
'id': <'Wired connection 1'>,
|
||||||
|
'type': <'802-3-ethernet'>
|
||||||
|
},
|
||||||
|
'ipv4':
|
||||||
|
{
|
||||||
|
'method': <'manual'>,
|
||||||
|
'dns': <[uint32 16951488]>,
|
||||||
|
'address-data': <[
|
||||||
|
{
|
||||||
|
'address': <'192.168.2.148'>,
|
||||||
|
'prefix': <uint32 24>
|
||||||
|
}]>,
|
||||||
|
'gateway': <'192.168.2.1'>
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
)
|
||||||
|
|
||||||
|
data = interface_update_payload(network_interface, **{"address": "1.1.1.1"})
|
||||||
|
assert DBus.parse_gvariant(data)["ipv4"]["address-data"][0]["address"] == "1.1.1.1"
|
Loading…
x
Reference in New Issue
Block a user