1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-30 04:05:01 +01:00
Files

149 lines
4.4 KiB
Python

"""The DNS IP integration."""
import asyncio
from dataclasses import dataclass
import logging
import aiodns
from aiodns.error import DNSError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from .const import (
CONF_HOSTNAME,
CONF_IPV4,
CONF_IPV6,
CONF_PORT_IPV6,
CONF_RESOLVER,
CONF_RESOLVER_IPV6,
DEFAULT_PORT,
PLATFORMS,
)
_LOGGER = logging.getLogger(__name__)
@dataclass
class DnsIPRuntimeData:
"""Runtime data for DNS IP integration."""
resolver_ipv4: aiodns.DNSResolver | None
resolver_ipv6: aiodns.DNSResolver | None
type DnsIPConfigEntry = ConfigEntry[DnsIPRuntimeData]
async def async_setup_entry(hass: HomeAssistant, entry: DnsIPConfigEntry) -> bool:
"""Set up DNS IP from a config entry."""
hostname = entry.data[CONF_HOSTNAME]
resolver_ipv4: aiodns.DNSResolver | None = None
resolver_ipv6: aiodns.DNSResolver | None = None
queries: list = []
if entry.data[CONF_IPV4]:
resolver_ipv4 = aiodns.DNSResolver(
nameservers=[entry.options[CONF_RESOLVER]],
tcp_port=entry.options[CONF_PORT],
udp_port=entry.options[CONF_PORT],
)
queries.append(resolver_ipv4.query(hostname, "A"))
if entry.data[CONF_IPV6]:
resolver_ipv6 = aiodns.DNSResolver(
nameservers=[entry.options[CONF_RESOLVER_IPV6]],
tcp_port=entry.options[CONF_PORT_IPV6],
udp_port=entry.options[CONF_PORT_IPV6],
)
queries.append(resolver_ipv6.query(hostname, "AAAA"))
async def _close_resolvers() -> None:
if resolver_ipv4 is not None:
await resolver_ipv4.close()
if resolver_ipv6 is not None:
await resolver_ipv6.close()
try:
async with asyncio.timeout(10):
results = await asyncio.gather(*queries, return_exceptions=True)
except TimeoutError as err:
await _close_resolvers()
raise ConfigEntryNotReady(
f"DNS lookup timed out for {hostname}: {err}"
) from err
errors = [
result for result in results if isinstance(result, (TimeoutError, DNSError))
]
if errors and len(errors) == len(results):
await _close_resolvers()
raise ConfigEntryNotReady(
f"DNS lookup failed for {hostname}: {errors[0]}"
) from errors[0]
entry.runtime_data = DnsIPRuntimeData(
resolver_ipv4=resolver_ipv4,
resolver_ipv6=resolver_ipv6,
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: DnsIPConfigEntry) -> bool:
"""Unload DNS IP config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
if entry.runtime_data.resolver_ipv4 is not None:
await entry.runtime_data.resolver_ipv4.close()
if entry.runtime_data.resolver_ipv6 is not None:
await entry.runtime_data.resolver_ipv6.close()
return unload_ok
async def async_migrate_entry(
hass: HomeAssistant, config_entry: DnsIPConfigEntry
) -> bool:
"""Migrate old entry to a newer version."""
if config_entry.version > 1:
# This means the user has downgraded from a future version
return False
if config_entry.version < 2 and config_entry.minor_version < 2:
_LOGGER.debug(
"Migrating configuration from version %s.%s",
config_entry.version,
config_entry.minor_version,
)
new_options = {**config_entry.options}
new_options[CONF_PORT] = DEFAULT_PORT
new_options[CONF_PORT_IPV6] = DEFAULT_PORT
hass.config_entries.async_update_entry(
config_entry, options=new_options, minor_version=2
)
_LOGGER.debug("Migration to configuration version %s.%s successful", 1, 2)
if config_entry.version < 2 and config_entry.minor_version < 3:
_LOGGER.debug(
"Migrating configuration from version %s.%s",
config_entry.version,
config_entry.minor_version,
)
hass.config_entries.async_update_entry(
config_entry, unique_id=None, minor_version=3
)
_LOGGER.debug("Migration to configuration version %s.%s successful", 1, 3)
return True