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."""
from enum import Enum
from enum import Enum, IntEnum
from socket import AF_INET, AF_INET6
DBUS_NAME_HAOS = "io.hass.os"
DBUS_NAME_HOSTNAME = "org.freedesktop.hostname1"
DBUS_NAME_LOGIND = "org.freedesktop.login1"
DBUS_NAME_NM = "org.freedesktop.NetworkManager"
DBUS_NAME_RAUC = "de.pengutronix.rauc"
DBUS_NAME_RESOLVED = "org.freedesktop.resolve1"
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
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_NM = "org.freedesktop.NetworkManager"
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_SYSTEMD_MANAGER = "org.freedesktop.systemd1.Manager"
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_LOGIND = "/org/freedesktop/login1"
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
DBUS_OBJECT_RESOLVED = "/org/freedesktop/resolve1"
DBUS_OBJECT_SETTINGS = "/org/freedesktop/NetworkManager/Settings"
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
@ -52,19 +56,33 @@ DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
DBUS_ATTR_ADDRESS_DATA = "AddressData"
DBUS_ATTR_BOOT_SLOT = "BootSlot"
DBUS_ATTR_CACHE_STATISTICS = "CacheStatistics"
DBUS_ATTR_CHASSIS = "Chassis"
DBUS_ATTR_COMPATIBLE = "Compatible"
DBUS_ATTR_CONFIGURATION = "Configuration"
DBUS_ATTR_CONNECTION = "Connection"
DBUS_ATTR_CONNECTION_ENABLED = "ConnectivityCheckEnabled"
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
DBUS_ATTR_CURRENT_DNS_SERVER = "CurrentDNSServer"
DBUS_ATTR_CURRENT_DNS_SERVER_EX = "CurrentDNSServerEx"
DBUS_ATTR_DEFAULT = "Default"
DBUS_ATTR_DEPLOYMENT = "Deployment"
DBUS_ATTR_DEVICE_INTERFACE = "Interface"
DBUS_ATTR_DEVICE_TYPE = "DeviceType"
DBUS_ATTR_DEVICES = "Devices"
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_FALLBACK_DNS = "FallbackDNS"
DBUS_ATTR_FALLBACK_DNS_EX = "FallbackDNSEx"
DBUS_ATTR_FINISH_TIMESTAMP = "FinishTimestamp"
DBUS_ATTR_FIRMWARE_TIMESTAMP_MONOTONIC = "FirmwareTimestampMonotonic"
DBUS_ATTR_FREQUENCY = "Frequency"
@ -76,11 +94,13 @@ DBUS_ATTR_IP6CONFIG = "Ip6Config"
DBUS_ATTR_KERNEL_RELEASE = "KernelRelease"
DBUS_ATTR_KERNEL_TIMESTAMP_MONOTONIC = "KernelTimestampMonotonic"
DBUS_ATTR_LAST_ERROR = "LastError"
DBUS_ATTR_LLMNR = "LLMNR"
DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname"
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
DBUS_ATTR_LOCALRTC = "LocalRTC"
DBUS_ATTR_MANAGED = "Managed"
DBUS_ATTR_MODE = "Mode"
DBUS_ATTR_MODE = "Mode"
DBUS_ATTR_MULTICAST_DNS = "MulticastDNS"
DBUS_ATTR_NAMESERVER_DATA = "NameserverData"
DBUS_ATTR_NAMESERVERS = "Nameservers"
DBUS_ATTR_NTP = "NTP"
@ -89,6 +109,7 @@ DBUS_ATTR_OPERATING_SYSTEM_PRETTY_NAME = "OperatingSystemPrettyName"
DBUS_ATTR_OPERATION = "Operation"
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
DBUS_ATTR_RESOLV_CONF_MODE = "ResolvConfMode"
DBUS_ATTR_RCMANAGER = "RcManager"
DBUS_ATTR_SSID = "Ssid"
DBUS_ATTR_STATE = "State"
@ -97,6 +118,7 @@ DBUS_ATTR_STATIC_OPERATING_SYSTEM_CPE_NAME = "OperatingSystemCPEName"
DBUS_ATTR_STRENGTH = "Strength"
DBUS_ATTR_TIMEUSEC = "TimeUSec"
DBUS_ATTR_TIMEZONE = "Timezone"
DBUS_ATTR_TRANSACTION_STATISTICS = "TransactionStatistics"
DBUS_ATTR_TYPE = "Type"
DBUS_ATTR_USERSPACE_TIMESTAMP_MONOTONIC = "UserspaceTimestampMonotonic"
DBUS_ATTR_UUID = "Uuid"
@ -176,3 +198,53 @@ class WirelessMethodType(int, Enum):
INFRASTRUCTURE = 2
ACCESSPOINT = 3
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 .network import NetworkManager
from .rauc import Rauc
from .resolved import Resolved
from .systemd import Systemd
from .timedate import TimeDate
@ -29,6 +30,7 @@ class DBusManager(CoreSysAttributes):
self._network: NetworkManager = NetworkManager()
self._agent: OSAgent = OSAgent()
self._timedate: TimeDate = TimeDate()
self._resolved: Resolved = Resolved()
@property
def systemd(self) -> Systemd:
@ -65,6 +67,11 @@ class DBusManager(CoreSysAttributes):
"""Return the timedate interface."""
return self._timedate
@property
def resolved(self) -> Resolved:
"""Return the resolved interface."""
return self._resolved
async def load(self) -> None:
"""Connect interfaces to D-Bus."""
if not SOCKET_DBUS.exists():
@ -81,6 +88,7 @@ class DBusManager(CoreSysAttributes):
self.timedate,
self.network,
self.rauc,
self.resolved,
]
for dbus in dbus_loads:
_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"
SOURCE_MODS = "source_mods"
SYSTEMD = "systemd"
SYSTEMD_RESOLVED = "systemd_resolved"
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()