Add unsupported message if no systemd-resolved (#3487)

* Require resolved for supported systems

* Added properties for dbus resolved
This commit is contained in:
Mike Degatano 2022-03-07 04:38:24 -05:00 committed by GitHub
parent b451e555d3
commit dd3a4a1f47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 702 additions and 2 deletions

View File

@ -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"

View File

@ -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
View 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)

View File

@ -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):

View 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
View 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

View 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>

View 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"
}

View 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()