mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-08 17:56:33 +00:00
Add unsupported message if no systemd-resolved (#3487)
* Require resolved for supported systems * Added properties for dbus resolved
This commit is contained in:
parent
b451e555d3
commit
dd3a4a1f47
@ -1,11 +1,13 @@
|
|||||||
"""Constants for DBUS."""
|
"""Constants for DBUS."""
|
||||||
from enum import Enum
|
from enum import Enum, IntEnum
|
||||||
|
from socket import AF_INET, AF_INET6
|
||||||
|
|
||||||
DBUS_NAME_HAOS = "io.hass.os"
|
DBUS_NAME_HAOS = "io.hass.os"
|
||||||
DBUS_NAME_HOSTNAME = "org.freedesktop.hostname1"
|
DBUS_NAME_HOSTNAME = "org.freedesktop.hostname1"
|
||||||
DBUS_NAME_LOGIND = "org.freedesktop.login1"
|
DBUS_NAME_LOGIND = "org.freedesktop.login1"
|
||||||
DBUS_NAME_NM = "org.freedesktop.NetworkManager"
|
DBUS_NAME_NM = "org.freedesktop.NetworkManager"
|
||||||
DBUS_NAME_RAUC = "de.pengutronix.rauc"
|
DBUS_NAME_RAUC = "de.pengutronix.rauc"
|
||||||
|
DBUS_NAME_RESOLVED = "org.freedesktop.resolve1"
|
||||||
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
|
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
|
||||||
DBUS_NAME_TIMEDATE = "org.freedesktop.timedate1"
|
DBUS_NAME_TIMEDATE = "org.freedesktop.timedate1"
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ DBUS_IFACE_IP4CONFIG = "org.freedesktop.NetworkManager.IP4Config"
|
|||||||
DBUS_IFACE_IP6CONFIG = "org.freedesktop.NetworkManager.IP6Config"
|
DBUS_IFACE_IP6CONFIG = "org.freedesktop.NetworkManager.IP6Config"
|
||||||
DBUS_IFACE_NM = "org.freedesktop.NetworkManager"
|
DBUS_IFACE_NM = "org.freedesktop.NetworkManager"
|
||||||
DBUS_IFACE_RAUC_INSTALLER = "de.pengutronix.rauc.Installer"
|
DBUS_IFACE_RAUC_INSTALLER = "de.pengutronix.rauc.Installer"
|
||||||
|
DBUS_IFACE_RESOLVED_MANAGER = "org.freedesktop.resolve1.Manager"
|
||||||
DBUS_IFACE_SETTINGS_CONNECTION = "org.freedesktop.NetworkManager.Settings.Connection"
|
DBUS_IFACE_SETTINGS_CONNECTION = "org.freedesktop.NetworkManager.Settings.Connection"
|
||||||
DBUS_IFACE_SYSTEMD_MANAGER = "org.freedesktop.systemd1.Manager"
|
DBUS_IFACE_SYSTEMD_MANAGER = "org.freedesktop.systemd1.Manager"
|
||||||
DBUS_IFACE_TIMEDATE = "org.freedesktop.timedate1"
|
DBUS_IFACE_TIMEDATE = "org.freedesktop.timedate1"
|
||||||
@ -43,6 +46,7 @@ DBUS_OBJECT_HAOS_SYSTEM = "/io/hass/os/System"
|
|||||||
DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1"
|
DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1"
|
||||||
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
|
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
|
||||||
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
|
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
|
||||||
|
DBUS_OBJECT_RESOLVED = "/org/freedesktop/resolve1"
|
||||||
DBUS_OBJECT_SETTINGS = "/org/freedesktop/NetworkManager/Settings"
|
DBUS_OBJECT_SETTINGS = "/org/freedesktop/NetworkManager/Settings"
|
||||||
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
|
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
|
||||||
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
|
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
|
||||||
@ -52,19 +56,33 @@ DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
|
|||||||
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
|
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
|
||||||
DBUS_ATTR_ADDRESS_DATA = "AddressData"
|
DBUS_ATTR_ADDRESS_DATA = "AddressData"
|
||||||
DBUS_ATTR_BOOT_SLOT = "BootSlot"
|
DBUS_ATTR_BOOT_SLOT = "BootSlot"
|
||||||
|
DBUS_ATTR_CACHE_STATISTICS = "CacheStatistics"
|
||||||
DBUS_ATTR_CHASSIS = "Chassis"
|
DBUS_ATTR_CHASSIS = "Chassis"
|
||||||
DBUS_ATTR_COMPATIBLE = "Compatible"
|
DBUS_ATTR_COMPATIBLE = "Compatible"
|
||||||
DBUS_ATTR_CONFIGURATION = "Configuration"
|
DBUS_ATTR_CONFIGURATION = "Configuration"
|
||||||
DBUS_ATTR_CONNECTION = "Connection"
|
DBUS_ATTR_CONNECTION = "Connection"
|
||||||
DBUS_ATTR_CONNECTION_ENABLED = "ConnectivityCheckEnabled"
|
DBUS_ATTR_CONNECTION_ENABLED = "ConnectivityCheckEnabled"
|
||||||
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
|
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
|
||||||
|
DBUS_ATTR_CURRENT_DNS_SERVER = "CurrentDNSServer"
|
||||||
|
DBUS_ATTR_CURRENT_DNS_SERVER_EX = "CurrentDNSServerEx"
|
||||||
DBUS_ATTR_DEFAULT = "Default"
|
DBUS_ATTR_DEFAULT = "Default"
|
||||||
DBUS_ATTR_DEPLOYMENT = "Deployment"
|
DBUS_ATTR_DEPLOYMENT = "Deployment"
|
||||||
DBUS_ATTR_DEVICE_INTERFACE = "Interface"
|
DBUS_ATTR_DEVICE_INTERFACE = "Interface"
|
||||||
DBUS_ATTR_DEVICE_TYPE = "DeviceType"
|
DBUS_ATTR_DEVICE_TYPE = "DeviceType"
|
||||||
DBUS_ATTR_DEVICES = "Devices"
|
DBUS_ATTR_DEVICES = "Devices"
|
||||||
DBUS_ATTR_DIAGNOSTICS = "Diagnostics"
|
DBUS_ATTR_DIAGNOSTICS = "Diagnostics"
|
||||||
|
DBUS_ATTR_DNS = "DNS"
|
||||||
|
DBUS_ATTR_DNS_EX = "DNSEx"
|
||||||
|
DBUS_ATTR_DNS_OVER_TLS = "DNSOverTLS"
|
||||||
|
DBUS_ATTR_DNS_STUB_LISTENER = "DNSStubListener"
|
||||||
|
DBUS_ATTR_DNSSEC = "DNSSEC"
|
||||||
|
DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS = "DNSSECNegativeTrustAnchors"
|
||||||
|
DBUS_ATTR_DNSSEC_STATISTICS = "DNSSECStatistics"
|
||||||
|
DBUS_ATTR_DNSSEC_SUPPORTED = "DNSSECSupported"
|
||||||
|
DBUS_ATTR_DOMAINS = "Domains"
|
||||||
DBUS_ATTR_DRIVER = "Driver"
|
DBUS_ATTR_DRIVER = "Driver"
|
||||||
|
DBUS_ATTR_FALLBACK_DNS = "FallbackDNS"
|
||||||
|
DBUS_ATTR_FALLBACK_DNS_EX = "FallbackDNSEx"
|
||||||
DBUS_ATTR_FINISH_TIMESTAMP = "FinishTimestamp"
|
DBUS_ATTR_FINISH_TIMESTAMP = "FinishTimestamp"
|
||||||
DBUS_ATTR_FIRMWARE_TIMESTAMP_MONOTONIC = "FirmwareTimestampMonotonic"
|
DBUS_ATTR_FIRMWARE_TIMESTAMP_MONOTONIC = "FirmwareTimestampMonotonic"
|
||||||
DBUS_ATTR_FREQUENCY = "Frequency"
|
DBUS_ATTR_FREQUENCY = "Frequency"
|
||||||
@ -76,11 +94,13 @@ DBUS_ATTR_IP6CONFIG = "Ip6Config"
|
|||||||
DBUS_ATTR_KERNEL_RELEASE = "KernelRelease"
|
DBUS_ATTR_KERNEL_RELEASE = "KernelRelease"
|
||||||
DBUS_ATTR_KERNEL_TIMESTAMP_MONOTONIC = "KernelTimestampMonotonic"
|
DBUS_ATTR_KERNEL_TIMESTAMP_MONOTONIC = "KernelTimestampMonotonic"
|
||||||
DBUS_ATTR_LAST_ERROR = "LastError"
|
DBUS_ATTR_LAST_ERROR = "LastError"
|
||||||
|
DBUS_ATTR_LLMNR = "LLMNR"
|
||||||
|
DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname"
|
||||||
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
|
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
|
||||||
DBUS_ATTR_LOCALRTC = "LocalRTC"
|
DBUS_ATTR_LOCALRTC = "LocalRTC"
|
||||||
DBUS_ATTR_MANAGED = "Managed"
|
DBUS_ATTR_MANAGED = "Managed"
|
||||||
DBUS_ATTR_MODE = "Mode"
|
DBUS_ATTR_MODE = "Mode"
|
||||||
DBUS_ATTR_MODE = "Mode"
|
DBUS_ATTR_MULTICAST_DNS = "MulticastDNS"
|
||||||
DBUS_ATTR_NAMESERVER_DATA = "NameserverData"
|
DBUS_ATTR_NAMESERVER_DATA = "NameserverData"
|
||||||
DBUS_ATTR_NAMESERVERS = "Nameservers"
|
DBUS_ATTR_NAMESERVERS = "Nameservers"
|
||||||
DBUS_ATTR_NTP = "NTP"
|
DBUS_ATTR_NTP = "NTP"
|
||||||
@ -89,6 +109,7 @@ DBUS_ATTR_OPERATING_SYSTEM_PRETTY_NAME = "OperatingSystemPrettyName"
|
|||||||
DBUS_ATTR_OPERATION = "Operation"
|
DBUS_ATTR_OPERATION = "Operation"
|
||||||
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
|
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
|
||||||
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
|
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
|
||||||
|
DBUS_ATTR_RESOLV_CONF_MODE = "ResolvConfMode"
|
||||||
DBUS_ATTR_RCMANAGER = "RcManager"
|
DBUS_ATTR_RCMANAGER = "RcManager"
|
||||||
DBUS_ATTR_SSID = "Ssid"
|
DBUS_ATTR_SSID = "Ssid"
|
||||||
DBUS_ATTR_STATE = "State"
|
DBUS_ATTR_STATE = "State"
|
||||||
@ -97,6 +118,7 @@ DBUS_ATTR_STATIC_OPERATING_SYSTEM_CPE_NAME = "OperatingSystemCPEName"
|
|||||||
DBUS_ATTR_STRENGTH = "Strength"
|
DBUS_ATTR_STRENGTH = "Strength"
|
||||||
DBUS_ATTR_TIMEUSEC = "TimeUSec"
|
DBUS_ATTR_TIMEUSEC = "TimeUSec"
|
||||||
DBUS_ATTR_TIMEZONE = "Timezone"
|
DBUS_ATTR_TIMEZONE = "Timezone"
|
||||||
|
DBUS_ATTR_TRANSACTION_STATISTICS = "TransactionStatistics"
|
||||||
DBUS_ATTR_TYPE = "Type"
|
DBUS_ATTR_TYPE = "Type"
|
||||||
DBUS_ATTR_USERSPACE_TIMESTAMP_MONOTONIC = "UserspaceTimestampMonotonic"
|
DBUS_ATTR_USERSPACE_TIMESTAMP_MONOTONIC = "UserspaceTimestampMonotonic"
|
||||||
DBUS_ATTR_UUID = "Uuid"
|
DBUS_ATTR_UUID = "Uuid"
|
||||||
@ -176,3 +198,53 @@ class WirelessMethodType(int, Enum):
|
|||||||
INFRASTRUCTURE = 2
|
INFRASTRUCTURE = 2
|
||||||
ACCESSPOINT = 3
|
ACCESSPOINT = 3
|
||||||
MESH = 4
|
MESH = 4
|
||||||
|
|
||||||
|
|
||||||
|
class DNSAddressFamily(IntEnum):
|
||||||
|
"""Address family for DNS server."""
|
||||||
|
|
||||||
|
INET = AF_INET
|
||||||
|
INET6 = AF_INET6
|
||||||
|
|
||||||
|
|
||||||
|
class MulticastProtocolEnabled(str, Enum):
|
||||||
|
"""Multicast protocol enabled or resolve."""
|
||||||
|
|
||||||
|
YES = "yes"
|
||||||
|
NO = "no"
|
||||||
|
RESOLVE = "resolve"
|
||||||
|
|
||||||
|
|
||||||
|
class DNSOverTLSEnabled(str, Enum):
|
||||||
|
"""DNS over TLS enabled."""
|
||||||
|
|
||||||
|
YES = "yes"
|
||||||
|
NO = "no"
|
||||||
|
OPPORTUNISTIC = "opportunistic"
|
||||||
|
|
||||||
|
|
||||||
|
class DNSSECValidation(str, Enum):
|
||||||
|
"""DNSSEC validation enforced."""
|
||||||
|
|
||||||
|
YES = "yes"
|
||||||
|
NO = "no"
|
||||||
|
ALLOW_DOWNGRADE = "allow-downgrade"
|
||||||
|
|
||||||
|
|
||||||
|
class DNSStubListenerEnabled(str, Enum):
|
||||||
|
"""DNS stub listener enabled."""
|
||||||
|
|
||||||
|
YES = "yes"
|
||||||
|
NO = "no"
|
||||||
|
TCP_ONLY = "tcp"
|
||||||
|
UDP_ONLY = "udp"
|
||||||
|
|
||||||
|
|
||||||
|
class ResolvConfMode(str, Enum):
|
||||||
|
"""Resolv.conf management mode."""
|
||||||
|
|
||||||
|
FOREIGN = "foreign"
|
||||||
|
MISSING = "missing"
|
||||||
|
STATIC = "static"
|
||||||
|
STUB = "stub"
|
||||||
|
UPLINK = "uplink"
|
||||||
|
@ -9,6 +9,7 @@ from .interface import DBusInterface
|
|||||||
from .logind import Logind
|
from .logind import Logind
|
||||||
from .network import NetworkManager
|
from .network import NetworkManager
|
||||||
from .rauc import Rauc
|
from .rauc import Rauc
|
||||||
|
from .resolved import Resolved
|
||||||
from .systemd import Systemd
|
from .systemd import Systemd
|
||||||
from .timedate import TimeDate
|
from .timedate import TimeDate
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ class DBusManager(CoreSysAttributes):
|
|||||||
self._network: NetworkManager = NetworkManager()
|
self._network: NetworkManager = NetworkManager()
|
||||||
self._agent: OSAgent = OSAgent()
|
self._agent: OSAgent = OSAgent()
|
||||||
self._timedate: TimeDate = TimeDate()
|
self._timedate: TimeDate = TimeDate()
|
||||||
|
self._resolved: Resolved = Resolved()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def systemd(self) -> Systemd:
|
def systemd(self) -> Systemd:
|
||||||
@ -65,6 +67,11 @@ class DBusManager(CoreSysAttributes):
|
|||||||
"""Return the timedate interface."""
|
"""Return the timedate interface."""
|
||||||
return self._timedate
|
return self._timedate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def resolved(self) -> Resolved:
|
||||||
|
"""Return the resolved interface."""
|
||||||
|
return self._resolved
|
||||||
|
|
||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Connect interfaces to D-Bus."""
|
"""Connect interfaces to D-Bus."""
|
||||||
if not SOCKET_DBUS.exists():
|
if not SOCKET_DBUS.exists():
|
||||||
@ -81,6 +88,7 @@ class DBusManager(CoreSysAttributes):
|
|||||||
self.timedate,
|
self.timedate,
|
||||||
self.network,
|
self.network,
|
||||||
self.rauc,
|
self.rauc,
|
||||||
|
self.resolved,
|
||||||
]
|
]
|
||||||
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)
|
||||||
|
188
supervisor/dbus/resolved.py
Normal file
188
supervisor/dbus/resolved.py
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
"""D-Bus interface for systemd-resolved."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ..exceptions import DBusError, DBusInterfaceError
|
||||||
|
from ..utils.dbus import DBus
|
||||||
|
from .const import (
|
||||||
|
DBUS_ATTR_CACHE_STATISTICS,
|
||||||
|
DBUS_ATTR_CURRENT_DNS_SERVER,
|
||||||
|
DBUS_ATTR_CURRENT_DNS_SERVER_EX,
|
||||||
|
DBUS_ATTR_DNS,
|
||||||
|
DBUS_ATTR_DNS_EX,
|
||||||
|
DBUS_ATTR_DNS_OVER_TLS,
|
||||||
|
DBUS_ATTR_DNS_STUB_LISTENER,
|
||||||
|
DBUS_ATTR_DNSSEC,
|
||||||
|
DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS,
|
||||||
|
DBUS_ATTR_DNSSEC_STATISTICS,
|
||||||
|
DBUS_ATTR_DNSSEC_SUPPORTED,
|
||||||
|
DBUS_ATTR_DOMAINS,
|
||||||
|
DBUS_ATTR_FALLBACK_DNS,
|
||||||
|
DBUS_ATTR_FALLBACK_DNS_EX,
|
||||||
|
DBUS_ATTR_LLMNR,
|
||||||
|
DBUS_ATTR_LLMNR_HOSTNAME,
|
||||||
|
DBUS_ATTR_MULTICAST_DNS,
|
||||||
|
DBUS_ATTR_RESOLV_CONF_MODE,
|
||||||
|
DBUS_ATTR_TRANSACTION_STATISTICS,
|
||||||
|
DBUS_IFACE_RESOLVED_MANAGER,
|
||||||
|
DBUS_NAME_RESOLVED,
|
||||||
|
DBUS_OBJECT_RESOLVED,
|
||||||
|
DNSAddressFamily,
|
||||||
|
DNSOverTLSEnabled,
|
||||||
|
DNSSECValidation,
|
||||||
|
DNSStubListenerEnabled,
|
||||||
|
MulticastProtocolEnabled,
|
||||||
|
ResolvConfMode,
|
||||||
|
)
|
||||||
|
from .interface import DBusInterface, dbus_property
|
||||||
|
from .utils import dbus_connected
|
||||||
|
|
||||||
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Resolved(DBusInterface):
|
||||||
|
"""Handle D-Bus interface for systemd-resolved."""
|
||||||
|
|
||||||
|
name = DBUS_NAME_RESOLVED
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize Properties."""
|
||||||
|
self.properties: dict[str, Any] = {}
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
"""Connect to D-Bus."""
|
||||||
|
try:
|
||||||
|
self.dbus = await DBus.connect(DBUS_NAME_RESOLVED, DBUS_OBJECT_RESOLVED)
|
||||||
|
except DBusError:
|
||||||
|
_LOGGER.warning("Can't connect to systemd-resolved.")
|
||||||
|
except DBusInterfaceError:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Host has no systemd-resolved support. DNS will not work correctly."
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def cache_statistics(self) -> tuple[int, int, int] | None:
|
||||||
|
"""Return current cache entries and hits and misses since last reset."""
|
||||||
|
return self.properties[DBUS_ATTR_CACHE_STATISTICS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def current_dns_server(
|
||||||
|
self,
|
||||||
|
) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
|
||||||
|
"""Return current DNS server."""
|
||||||
|
return self.properties[DBUS_ATTR_CURRENT_DNS_SERVER]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def current_dns_server_ex(
|
||||||
|
self,
|
||||||
|
) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
|
||||||
|
"""Return current DNS server including port and server name."""
|
||||||
|
return self.properties[DBUS_ATTR_CURRENT_DNS_SERVER_EX]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dns(self) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
|
||||||
|
"""Return DNS servers in use."""
|
||||||
|
return self.properties[DBUS_ATTR_DNS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dns_ex(self) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
|
||||||
|
"""Return DNS servers in use including port and server name."""
|
||||||
|
return self.properties[DBUS_ATTR_DNS_EX]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dns_over_tls(self) -> DNSOverTLSEnabled | None:
|
||||||
|
"""Return DNS over TLS enabled."""
|
||||||
|
return self.properties[DBUS_ATTR_DNS_OVER_TLS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dns_stub_listener(self) -> DNSStubListenerEnabled | None:
|
||||||
|
"""Return DNS stub listener enabled on port 53."""
|
||||||
|
return self.properties[DBUS_ATTR_DNS_STUB_LISTENER]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dnssec(self) -> DNSSECValidation | None:
|
||||||
|
"""Return DNSSEC validation enforced."""
|
||||||
|
return self.properties[DBUS_ATTR_DNSSEC]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dnssec_negative_trust_anchors(self) -> list[str] | None:
|
||||||
|
"""Return DNSSEC negative trust anchors."""
|
||||||
|
return self.properties[DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dnssec_statistics(self) -> tuple[int, int, int, int] | None:
|
||||||
|
"""Return Secure, insecure, bogus, and indeterminate DNSSEC validations since last reset."""
|
||||||
|
return self.properties[DBUS_ATTR_DNSSEC_STATISTICS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def dnssec_supported(self) -> bool | None:
|
||||||
|
"""Return DNSSEC enabled and selected DNS servers support it."""
|
||||||
|
return self.properties[DBUS_ATTR_DNSSEC_SUPPORTED]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def domains(self) -> list[tuple[int, str, bool]] | None:
|
||||||
|
"""Return search and routing domains in use."""
|
||||||
|
return self.properties[DBUS_ATTR_DOMAINS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def fallback_dns(self) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
|
||||||
|
"""Return fallback DNS servers."""
|
||||||
|
return self.properties[DBUS_ATTR_FALLBACK_DNS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def fallback_dns_ex(
|
||||||
|
self,
|
||||||
|
) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
|
||||||
|
"""Return fallback DNS servers including port and server name."""
|
||||||
|
return self.properties[DBUS_ATTR_FALLBACK_DNS_EX]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def llmnr(self) -> MulticastProtocolEnabled | None:
|
||||||
|
"""Return LLMNR enabled."""
|
||||||
|
return self.properties[DBUS_ATTR_LLMNR]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def llmnr_hostname(self) -> str | None:
|
||||||
|
"""Return LLMNR hostname on network."""
|
||||||
|
return self.properties[DBUS_ATTR_LLMNR_HOSTNAME]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def multicast_dns(self) -> MulticastProtocolEnabled | None:
|
||||||
|
"""Return MDNS enabled."""
|
||||||
|
return self.properties[DBUS_ATTR_MULTICAST_DNS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def resolv_conf_mode(self) -> ResolvConfMode | None:
|
||||||
|
"""Return how /etc/resolv.conf managed on host."""
|
||||||
|
return self.properties[DBUS_ATTR_RESOLV_CONF_MODE]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@dbus_property
|
||||||
|
def transaction_statistics(self) -> tuple[int, int] | None:
|
||||||
|
"""Return transactions processing and processed since last reset."""
|
||||||
|
return self.properties[DBUS_ATTR_TRANSACTION_STATISTICS]
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
async def update(self):
|
||||||
|
"""Update Properties."""
|
||||||
|
self.properties = await self.dbus.get_properties(DBUS_IFACE_RESOLVED_MANAGER)
|
@ -41,6 +41,7 @@ class UnsupportedReason(str, Enum):
|
|||||||
SOFTWARE = "software"
|
SOFTWARE = "software"
|
||||||
SOURCE_MODS = "source_mods"
|
SOURCE_MODS = "source_mods"
|
||||||
SYSTEMD = "systemd"
|
SYSTEMD = "systemd"
|
||||||
|
SYSTEMD_RESOLVED = "systemd_resolved"
|
||||||
|
|
||||||
|
|
||||||
class UnhealthyReason(str, Enum):
|
class UnhealthyReason(str, Enum):
|
||||||
|
34
supervisor/resolution/evaluations/resolved.py
Normal file
34
supervisor/resolution/evaluations/resolved.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""Evaluation class for systemd-resolved."""
|
||||||
|
|
||||||
|
from ...const import CoreState
|
||||||
|
from ...coresys import CoreSys
|
||||||
|
from ..const import UnsupportedReason
|
||||||
|
from .base import EvaluateBase
|
||||||
|
|
||||||
|
|
||||||
|
def setup(coresys: CoreSys) -> EvaluateBase:
|
||||||
|
"""Initialize evaluation-setup function."""
|
||||||
|
return EvaluateResolved(coresys)
|
||||||
|
|
||||||
|
|
||||||
|
class EvaluateResolved(EvaluateBase):
|
||||||
|
"""Evaluate systemd-resolved."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reason(self) -> UnsupportedReason:
|
||||||
|
"""Return a UnsupportedReason enum."""
|
||||||
|
return UnsupportedReason.SYSTEMD_RESOLVED
|
||||||
|
|
||||||
|
@property
|
||||||
|
def on_failure(self) -> str:
|
||||||
|
"""Return a string that is printed when self.evaluate is False."""
|
||||||
|
return "Systemd-Resolved is required for DNS in Home Assistant."
|
||||||
|
|
||||||
|
@property
|
||||||
|
def states(self) -> list[CoreState]:
|
||||||
|
"""Return a list of valid states when this evaluation can run."""
|
||||||
|
return [CoreState.SETUP]
|
||||||
|
|
||||||
|
async def evaluate(self) -> bool:
|
||||||
|
"""Run evaluation."""
|
||||||
|
return not self.sys_dbus.resolved.is_connected
|
115
tests/dbus/test_resolved.py
Normal file
115
tests/dbus/test_resolved.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
"""Test systemd-resolved dbus interface."""
|
||||||
|
|
||||||
|
from socket import AF_INET6, inet_aton, inet_pton
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from supervisor.coresys import CoreSys
|
||||||
|
from supervisor.dbus.const import (
|
||||||
|
DNSOverTLSEnabled,
|
||||||
|
DNSSECValidation,
|
||||||
|
DNSStubListenerEnabled,
|
||||||
|
MulticastProtocolEnabled,
|
||||||
|
ResolvConfMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
DNS_IP_FIELDS = [
|
||||||
|
"DNS",
|
||||||
|
"DNSEx",
|
||||||
|
"FallbackDNS",
|
||||||
|
"FallbackDNSEx",
|
||||||
|
"CurrentDNSServer",
|
||||||
|
"CurrentDNSServerEx",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="coresys_ip_bytes")
|
||||||
|
async def fixture_coresys_ip_bytes(coresys: CoreSys) -> CoreSys:
|
||||||
|
"""Coresys with ip addresses correctly mocked as bytes."""
|
||||||
|
get_properties = coresys.dbus.network.dbus.get_properties
|
||||||
|
|
||||||
|
async def mock_get_properties(dbus_obj, interface):
|
||||||
|
reply = await get_properties(interface)
|
||||||
|
|
||||||
|
for field in DNS_IP_FIELDS:
|
||||||
|
if field in reply and len(reply[field]) > 0:
|
||||||
|
if isinstance(reply[field][0], list):
|
||||||
|
for entry in reply[field]:
|
||||||
|
entry[2] = bytes(entry[2])
|
||||||
|
else:
|
||||||
|
reply[field][2] = bytes(reply[field][2])
|
||||||
|
|
||||||
|
return reply
|
||||||
|
|
||||||
|
with patch("supervisor.utils.dbus.DBus.get_properties", new=mock_get_properties):
|
||||||
|
yield coresys
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dbus_resolved_info(coresys_ip_bytes: CoreSys):
|
||||||
|
"""Test systemd-resolved dbus connection."""
|
||||||
|
coresys = coresys_ip_bytes
|
||||||
|
|
||||||
|
assert coresys.dbus.resolved.dns is None
|
||||||
|
|
||||||
|
await coresys.dbus.resolved.connect()
|
||||||
|
await coresys.dbus.resolved.update()
|
||||||
|
|
||||||
|
assert coresys.dbus.resolved.llmnr_hostname == "homeassistant"
|
||||||
|
assert coresys.dbus.resolved.llmnr == MulticastProtocolEnabled.YES
|
||||||
|
assert coresys.dbus.resolved.multicast_dns == MulticastProtocolEnabled.RESOLVE
|
||||||
|
assert coresys.dbus.resolved.dns_over_tls == DNSOverTLSEnabled.NO
|
||||||
|
|
||||||
|
assert len(coresys.dbus.resolved.dns) == 2
|
||||||
|
assert coresys.dbus.resolved.dns[0] == [0, 2, inet_aton("127.0.0.1")]
|
||||||
|
assert coresys.dbus.resolved.dns[1] == [0, 10, inet_pton(AF_INET6, "::1")]
|
||||||
|
assert len(coresys.dbus.resolved.dns_ex) == 2
|
||||||
|
assert coresys.dbus.resolved.dns_ex[0] == [0, 2, inet_aton("127.0.0.1"), 0, ""]
|
||||||
|
assert coresys.dbus.resolved.dns_ex[1] == [0, 10, inet_pton(AF_INET6, "::1"), 0, ""]
|
||||||
|
|
||||||
|
assert len(coresys.dbus.resolved.fallback_dns) == 2
|
||||||
|
assert coresys.dbus.resolved.fallback_dns[0] == [0, 2, inet_aton("1.1.1.1")]
|
||||||
|
assert coresys.dbus.resolved.fallback_dns[1] == [
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
inet_pton(AF_INET6, "2606:4700:4700::1111"),
|
||||||
|
]
|
||||||
|
assert len(coresys.dbus.resolved.fallback_dns_ex) == 2
|
||||||
|
assert coresys.dbus.resolved.fallback_dns_ex[0] == [
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
inet_aton("1.1.1.1"),
|
||||||
|
0,
|
||||||
|
"cloudflare-dns.com",
|
||||||
|
]
|
||||||
|
assert coresys.dbus.resolved.fallback_dns_ex[1] == [
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
inet_pton(AF_INET6, "2606:4700:4700::1111"),
|
||||||
|
0,
|
||||||
|
"cloudflare-dns.com",
|
||||||
|
]
|
||||||
|
|
||||||
|
assert coresys.dbus.resolved.current_dns_server == [0, 2, inet_aton("127.0.0.1")]
|
||||||
|
assert coresys.dbus.resolved.current_dns_server_ex == [
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
inet_aton("127.0.0.1"),
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(coresys.dbus.resolved.domains) == 1
|
||||||
|
assert coresys.dbus.resolved.domains[0] == [0, "local.hass.io", False]
|
||||||
|
|
||||||
|
assert coresys.dbus.resolved.transaction_statistics == [0, 100000]
|
||||||
|
assert coresys.dbus.resolved.cache_statistics == [10, 50000, 10000]
|
||||||
|
assert coresys.dbus.resolved.dnssec == DNSSECValidation.NO
|
||||||
|
assert coresys.dbus.resolved.dnssec_statistics == [0, 0, 0, 0]
|
||||||
|
assert coresys.dbus.resolved.dnssec_supported is False
|
||||||
|
assert coresys.dbus.resolved.dnssec_negative_trust_anchors == [
|
||||||
|
"168.192.in-addr.arpa",
|
||||||
|
"local",
|
||||||
|
]
|
||||||
|
assert coresys.dbus.resolved.dns_stub_listener == DNSStubListenerEnabled.NO
|
||||||
|
assert coresys.dbus.resolved.resolv_conf_mode == ResolvConfMode.FOREIGN
|
194
tests/fixtures/org_freedesktop_resolve1.xml
vendored
Normal file
194
tests/fixtures/org_freedesktop_resolve1.xml
vendored
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.freedesktop.DBus.Peer">
|
||||||
|
<method name="Ping"/>
|
||||||
|
<method name="GetMachineId">
|
||||||
|
<arg type="s" name="machine_uuid" direction="out"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.freedesktop.DBus.Introspectable">
|
||||||
|
<method name="Introspect">
|
||||||
|
<arg name="data" type="s" direction="out"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.freedesktop.DBus.Properties">
|
||||||
|
<method name="Get">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="property" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="out" type="v"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetAll">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="properties" direction="out" type="a{sv}"/>
|
||||||
|
</method>
|
||||||
|
<method name="Set">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="property" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="in" type="v"/>
|
||||||
|
</method>
|
||||||
|
<signal name="PropertiesChanged">
|
||||||
|
<arg type="s" name="interface"/>
|
||||||
|
<arg type="a{sv}" name="changed_properties"/>
|
||||||
|
<arg type="as" name="invalidated_properties"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
<interface name="org.freedesktop.resolve1.Manager">
|
||||||
|
<property name="LLMNRHostname" type="s" access="read">
|
||||||
|
</property>
|
||||||
|
<property name="LLMNR" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="MulticastDNS" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSOverTLS" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNS" type="a(iiay)" access="read">
|
||||||
|
</property>
|
||||||
|
<property name="DNSEx" type="a(iiayqs)" access="read">
|
||||||
|
</property>
|
||||||
|
<property name="FallbackDNS" type="a(iiay)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
||||||
|
</property>
|
||||||
|
<property name="FallbackDNSEx" type="a(iiayqs)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
||||||
|
</property>
|
||||||
|
<property name="CurrentDNSServer" type="(iiay)" access="read">
|
||||||
|
</property>
|
||||||
|
<property name="CurrentDNSServerEx" type="(iiayqs)" access="read">
|
||||||
|
</property>
|
||||||
|
<property name="Domains" type="a(isb)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="TransactionStatistics" type="(tt)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="CacheStatistics" type="(ttt)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSSEC" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSSECStatistics" type="(tttt)" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSSECSupported" type="b" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSSECNegativeTrustAnchors" type="as" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="DNSStubListener" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<property name="ResolvConfMode" type="s" access="read">
|
||||||
|
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||||
|
</property>
|
||||||
|
<method name="ResolveHostname">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="name" direction="in"/>
|
||||||
|
<arg type="i" name="family" direction="in"/>
|
||||||
|
<arg type="t" name="flags" direction="in"/>
|
||||||
|
<arg type="a(iiay)" name="addresses" direction="out"/>
|
||||||
|
<arg type="s" name="canonical" direction="out"/>
|
||||||
|
<arg type="t" name="flags" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="ResolveAddress">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="i" name="family" direction="in"/>
|
||||||
|
<arg type="ay" name="address" direction="in"/>
|
||||||
|
<arg type="t" name="flags" direction="in"/>
|
||||||
|
<arg type="a(is)" name="names" direction="out"/>
|
||||||
|
<arg type="t" name="flags" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="ResolveRecord">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="name" direction="in"/>
|
||||||
|
<arg type="q" name="class" direction="in"/>
|
||||||
|
<arg type="q" name="type" direction="in"/>
|
||||||
|
<arg type="t" name="flags" direction="in"/>
|
||||||
|
<arg type="a(iqqay)" name="records" direction="out"/>
|
||||||
|
<arg type="t" name="flags" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="ResolveService">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="name" direction="in"/>
|
||||||
|
<arg type="s" name="type" direction="in"/>
|
||||||
|
<arg type="s" name="domain" direction="in"/>
|
||||||
|
<arg type="i" name="family" direction="in"/>
|
||||||
|
<arg type="t" name="flags" direction="in"/>
|
||||||
|
<arg type="a(qqqsa(iiay)s)" name="srv_data" direction="out"/>
|
||||||
|
<arg type="aay" name="txt_data" direction="out"/>
|
||||||
|
<arg type="s" name="canonical_name" direction="out"/>
|
||||||
|
<arg type="s" name="canonical_type" direction="out"/>
|
||||||
|
<arg type="s" name="canonical_domain" direction="out"/>
|
||||||
|
<arg type="t" name="flags" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetLink">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="o" name="path" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDNS">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="a(iay)" name="addresses" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDNSEx">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="a(iayqs)" name="addresses" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDomains">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="a(sb)" name="domains" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDefaultRoute">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="b" name="enable" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkLLMNR">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="mode" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkMulticastDNS">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="mode" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDNSOverTLS">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="mode" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDNSSEC">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="s" name="mode" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="SetLinkDNSSECNegativeTrustAnchors">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
<arg type="as" name="names" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="RevertLink">
|
||||||
|
<arg type="i" name="ifindex" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="RegisterService">
|
||||||
|
<arg type="s" name="name" direction="in"/>
|
||||||
|
<arg type="s" name="name_template" direction="in"/>
|
||||||
|
<arg type="s" name="type" direction="in"/>
|
||||||
|
<arg type="q" name="service_port" direction="in"/>
|
||||||
|
<arg type="q" name="service_priority" direction="in"/>
|
||||||
|
<arg type="q" name="service_weight" direction="in"/>
|
||||||
|
<arg type="aa{say}" name="txt_datas" direction="in"/>
|
||||||
|
<arg type="o" name="service_path" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="UnregisterService">
|
||||||
|
<arg type="o" name="service_path" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="ResetStatistics">
|
||||||
|
</method>
|
||||||
|
<method name="FlushCaches">
|
||||||
|
</method>
|
||||||
|
<method name="ResetServerFeatures">
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
<node name="link"/>
|
||||||
|
<node name="dnssd"/>
|
||||||
|
</node>
|
39
tests/fixtures/org_freedesktop_resolve1_Manager.json
vendored
Normal file
39
tests/fixtures/org_freedesktop_resolve1_Manager.json
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"LLMNRHostname": "homeassistant",
|
||||||
|
"LLMNR": "yes",
|
||||||
|
"MulticastDNS": "resolve",
|
||||||
|
"DNSOverTLS": "no",
|
||||||
|
"DNS": [
|
||||||
|
[0, 2, [127, 0, 0, 1]],
|
||||||
|
[0, 10, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]
|
||||||
|
],
|
||||||
|
"DNSEx": [
|
||||||
|
[0, 2, [127, 0, 0, 1], 0, ""],
|
||||||
|
[0, 10, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 0, ""]
|
||||||
|
],
|
||||||
|
"FallbackDNS": [
|
||||||
|
[0, 2, [1, 1, 1, 1]],
|
||||||
|
[0, 10, [38, 6, 71, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17]]
|
||||||
|
],
|
||||||
|
"FallbackDNSEx": [
|
||||||
|
[0, 2, [1, 1, 1, 1], 0, "cloudflare-dns.com"],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
[38, 6, 71, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17],
|
||||||
|
0,
|
||||||
|
"cloudflare-dns.com"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"CurrentDNSServer": [0, 2, [127, 0, 0, 1]],
|
||||||
|
"CurrentDNSServerEx": [0, 2, [127, 0, 0, 1], 0, ""],
|
||||||
|
"Domains": [[0, "local.hass.io", false]],
|
||||||
|
"TransactionStatistics": [0, 100000],
|
||||||
|
"CacheStatistics": [10, 50000, 10000],
|
||||||
|
"DNSSEC": "no",
|
||||||
|
"DNSSECStatistics": [0, 0, 0, 0],
|
||||||
|
"DNSSECSupported": false,
|
||||||
|
"DNSSECNegativeTrustAnchors": ["168.192.in-addr.arpa", "local"],
|
||||||
|
"DNSStubListener": "no",
|
||||||
|
"ResolvConfMode": "foreign"
|
||||||
|
}
|
49
tests/resolution/evaluation/test_evaluate_resolved.py
Normal file
49
tests/resolution/evaluation/test_evaluate_resolved.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"""Test evaluate systemd-resolved."""
|
||||||
|
|
||||||
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
|
from supervisor.const import CoreState
|
||||||
|
from supervisor.coresys import CoreSys
|
||||||
|
from supervisor.resolution.evaluations.resolved import EvaluateResolved
|
||||||
|
|
||||||
|
|
||||||
|
async def test_evaluation(coresys: CoreSys):
|
||||||
|
"""Test evaluation."""
|
||||||
|
resolved = EvaluateResolved(coresys)
|
||||||
|
coresys.core.state = CoreState.SETUP
|
||||||
|
|
||||||
|
assert resolved.reason not in coresys.resolution.unsupported
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
type(coresys.dbus.resolved), "is_connected", PropertyMock(return_value=False)
|
||||||
|
):
|
||||||
|
await resolved()
|
||||||
|
assert resolved.reason in coresys.resolution.unsupported
|
||||||
|
|
||||||
|
await resolved()
|
||||||
|
assert resolved.reason not in coresys.resolution.unsupported
|
||||||
|
|
||||||
|
|
||||||
|
async def test_did_run(coresys: CoreSys):
|
||||||
|
"""Test that the evaluation ran as expected."""
|
||||||
|
resolved = EvaluateResolved(coresys)
|
||||||
|
should_run = resolved.states
|
||||||
|
should_not_run = [state for state in CoreState if state not in should_run]
|
||||||
|
assert len(should_run) != 0
|
||||||
|
assert len(should_not_run) != 0
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"supervisor.resolution.evaluations.resolved.EvaluateResolved.evaluate",
|
||||||
|
return_value=None,
|
||||||
|
) as evaluate:
|
||||||
|
for state in should_run:
|
||||||
|
coresys.core.state = state
|
||||||
|
await resolved()
|
||||||
|
evaluate.assert_called_once()
|
||||||
|
evaluate.reset_mock()
|
||||||
|
|
||||||
|
for state in should_not_run:
|
||||||
|
coresys.core.state = state
|
||||||
|
await resolved()
|
||||||
|
evaluate.assert_not_called()
|
||||||
|
evaluate.reset_mock()
|
Loading…
x
Reference in New Issue
Block a user