From 4d1c5c43559922745b218e8ba980adfef404a02e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Jan 2022 14:26:49 -1000 Subject: [PATCH 1/2] Handle no enabled ipv4 addresses in the network integration Fixes ``` 2022-01-03 13:46:08 ERROR (MainThread) [homeassistant.setup] Error during setup of component http Traceback (most recent call last): File "/srv/homeassistant/lib/python3.9/site-packages/homeassistant/setup.py", line 229, in _async_setup_component result = await task File "/srv/homeassistant/lib/python3.9/site-packages/homeassistant/components/http/__init__.py", line 192, in async_setup local_ip = await async_get_source_ip(hass) File "/srv/homeassistant/lib/python3.9/site-packages/homeassistant/components/network/__init__.py", line 35, in async_get_source_ip return source_ip if source_ip in all_ipv4s else all_ipv4s[0] IndexError: list index out of range ``` Reported here: https://community.home-assistant.io/t/2021-12-new-configuration-menu-the-button-entity-and-gorgeous-area-cards/365680/776?u=bdraco --- homeassistant/components/network/__init__.py | 9 ++++++++ tests/components/network/test_init.py | 23 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/homeassistant/components/network/__init__.py b/homeassistant/components/network/__init__.py index 7cc864727d73c..3e28c5add4cf7 100644 --- a/homeassistant/components/network/__init__.py +++ b/homeassistant/components/network/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from ipaddress import IPv4Address, IPv6Address, ip_interface +import logging from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.typing import ConfigType @@ -12,6 +13,8 @@ from .models import Adapter from .network import Network, async_get_network +_LOGGER = logging.getLogger(__name__) + @bind_hass async def async_get_adapters(hass: HomeAssistant) -> list[Adapter]: @@ -32,6 +35,12 @@ async def async_get_source_ip( all_ipv4s.extend([ipv4["address"] for ipv4 in ipv4s]) source_ip = util.async_get_source_ip(target_ip) + if not all_ipv4s: + _LOGGER.warning( + "Because the system does not have any enabled IPv4 addresses, source address detection may be inaccurate" + ) + return source_ip + return source_ip if source_ip in all_ipv4s else all_ipv4s[0] diff --git a/tests/components/network/test_init.py b/tests/components/network/test_init.py index 5a6802a14fbcb..4f3c313754ec4 100644 --- a/tests/components/network/test_init.py +++ b/tests/components/network/test_init.py @@ -602,3 +602,26 @@ async def test_async_get_ipv4_broadcast_addresses_multiple(hass, hass_storage): IPv4Address("192.168.1.255"), IPv4Address("169.254.255.255"), } + + +async def test_async_get_source_ip_no_enabled_addresses(hass, hass_storage, caplog): + """Test getting the source ip address when all adapters are disabled.""" + hass_storage[STORAGE_KEY] = { + "version": STORAGE_VERSION, + "key": STORAGE_KEY, + "data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]}, + } + + with patch( + "homeassistant.components.network.util.ifaddr.get_adapters", + return_value=[], + ), patch( + "homeassistant.components.network.util.socket.socket", + return_value=_mock_socket(["192.168.1.5"]), + ): + assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + + assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5" + + assert "source address detection may be inaccurate" in caplog.text From fa96ed771bfd79a45a9787e88d0205aea69b79e8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Jan 2022 14:42:04 -1000 Subject: [PATCH 2/2] Handle the total failure case --- homeassistant/components/network/__init__.py | 5 ++++ tests/components/network/test_init.py | 25 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/homeassistant/components/network/__init__.py b/homeassistant/components/network/__init__.py index 3e28c5add4cf7..b3ef88e7ab272 100644 --- a/homeassistant/components/network/__init__.py +++ b/homeassistant/components/network/__init__.py @@ -5,6 +5,7 @@ import logging from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass @@ -39,6 +40,10 @@ async def async_get_source_ip( _LOGGER.warning( "Because the system does not have any enabled IPv4 addresses, source address detection may be inaccurate" ) + if source_ip is None: + raise HomeAssistantError( + "Could not determine source ip because the system does not have any enabled IPv4 addresses and creating a socket failed" + ) return source_ip return source_ip if source_ip in all_ipv4s else all_ipv4s[0] diff --git a/tests/components/network/test_init.py b/tests/components/network/test_init.py index 4f3c313754ec4..1103c6fa85021 100644 --- a/tests/components/network/test_init.py +++ b/tests/components/network/test_init.py @@ -3,6 +3,7 @@ from unittest.mock import MagicMock, Mock, patch import ifaddr +import pytest from homeassistant.components import network from homeassistant.components.network.const import ( @@ -13,6 +14,7 @@ STORAGE_KEY, STORAGE_VERSION, ) +from homeassistant.exceptions import HomeAssistantError from homeassistant.setup import async_setup_component _NO_LOOPBACK_IPADDR = "192.168.1.5" @@ -625,3 +627,26 @@ async def test_async_get_source_ip_no_enabled_addresses(hass, hass_storage, capl assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5" assert "source address detection may be inaccurate" in caplog.text + + +async def test_async_get_source_ip_cannot_be_determined_and_no_enabled_addresses( + hass, hass_storage, caplog +): + """Test getting the source ip address when all adapters are disabled and getting it fails.""" + hass_storage[STORAGE_KEY] = { + "version": STORAGE_VERSION, + "key": STORAGE_KEY, + "data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]}, + } + + with patch( + "homeassistant.components.network.util.ifaddr.get_adapters", + return_value=[], + ), patch( + "homeassistant.components.network.util.socket.socket", + return_value=_mock_socket([None]), + ): + assert not await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + with pytest.raises(HomeAssistantError): + await network.async_get_source_ip(hass, MDNS_TARGET_IP)