Fix thread diagnostics loading blocking the event loop (#89307)
* Fix thread diagnostics loading blocking the event loop * patch target
This commit is contained in:
parent
099f16f6b8
commit
bde40cde48
2 changed files with 56 additions and 46 deletions
|
@ -17,9 +17,8 @@ some of their thread accessories can't be pinged, but it's still a thread proble
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, TypedDict
|
from typing import TYPE_CHECKING, Any, TypedDict
|
||||||
|
|
||||||
from pyroute2 import NDB # pylint: disable=no-name-in-module
|
|
||||||
from python_otbr_api.tlv_parser import MeshcopTLVType
|
from python_otbr_api.tlv_parser import MeshcopTLVType
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
|
@ -29,6 +28,9 @@ from homeassistant.core import HomeAssistant
|
||||||
from .dataset_store import async_get_store
|
from .dataset_store import async_get_store
|
||||||
from .discovery import async_read_zeroconf_cache
|
from .discovery import async_read_zeroconf_cache
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pyroute2 import NDB # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
|
|
||||||
class Neighbour(TypedDict):
|
class Neighbour(TypedDict):
|
||||||
"""A neighbour cache entry (ip neigh)."""
|
"""A neighbour cache entry (ip neigh)."""
|
||||||
|
@ -67,16 +69,15 @@ class Network(TypedDict):
|
||||||
unexpected_routers: set[str]
|
unexpected_routers: set[str]
|
||||||
|
|
||||||
|
|
||||||
def _get_possible_thread_routes() -> (
|
def _get_possible_thread_routes(
|
||||||
tuple[dict[str, dict[str, Route]], dict[str, set[str]]]
|
ndb: NDB,
|
||||||
):
|
) -> tuple[dict[str, dict[str, Route]], dict[str, set[str]]]:
|
||||||
# Build a list of possible thread routes
|
# Build a list of possible thread routes
|
||||||
# Right now, this is ipv6 /64's that have a gateway
|
# Right now, this is ipv6 /64's that have a gateway
|
||||||
# We cross reference with zerconf data to confirm which via's are known border routers
|
# We cross reference with zerconf data to confirm which via's are known border routers
|
||||||
routes: dict[str, dict[str, Route]] = {}
|
routes: dict[str, dict[str, Route]] = {}
|
||||||
reverse_routes: dict[str, set[str]] = {}
|
reverse_routes: dict[str, set[str]] = {}
|
||||||
|
|
||||||
with NDB() as ndb:
|
|
||||||
for record in ndb.routes:
|
for record in ndb.routes:
|
||||||
# Limit to IPV6 routes
|
# Limit to IPV6 routes
|
||||||
if record.family != 10:
|
if record.family != 10:
|
||||||
|
@ -100,25 +101,37 @@ def _get_possible_thread_routes() -> (
|
||||||
return routes, reverse_routes
|
return routes, reverse_routes
|
||||||
|
|
||||||
|
|
||||||
def _get_neighbours() -> dict[str, Neighbour]:
|
def _get_neighbours(ndb: NDB) -> dict[str, Neighbour]:
|
||||||
neighbours: dict[str, Neighbour] = {}
|
# Build a list of neighbours
|
||||||
|
neighbours: dict[str, Neighbour] = {
|
||||||
with NDB() as ndb:
|
record.dst: {
|
||||||
for record in ndb.neighbours:
|
|
||||||
neighbours[record.dst] = {
|
|
||||||
"lladdr": record.lladdr,
|
"lladdr": record.lladdr,
|
||||||
"state": record.state,
|
"state": record.state,
|
||||||
"probes": record.probes,
|
"probes": record.probes,
|
||||||
}
|
}
|
||||||
|
for record in ndb.neighbours
|
||||||
|
}
|
||||||
return neighbours
|
return neighbours
|
||||||
|
|
||||||
|
|
||||||
|
def _get_routes_and_neighbors():
|
||||||
|
"""Get the routes and neighbours from pyroute2."""
|
||||||
|
# Import in the executor since import NDB can take a while
|
||||||
|
from pyroute2 import ( # pylint: disable=no-name-in-module, import-outside-toplevel
|
||||||
|
NDB,
|
||||||
|
)
|
||||||
|
|
||||||
|
with NDB() as ndb: # pylint: disable=not-callable
|
||||||
|
routes, reverse_routes = _get_possible_thread_routes(ndb)
|
||||||
|
neighbours = _get_neighbours(ndb)
|
||||||
|
|
||||||
|
return routes, reverse_routes, neighbours
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: ConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for all known thread networks."""
|
"""Return diagnostics for all known thread networks."""
|
||||||
|
|
||||||
networks: dict[str, Network] = {}
|
networks: dict[str, Network] = {}
|
||||||
|
|
||||||
# Start with all networks that HA knows about
|
# Start with all networks that HA knows about
|
||||||
|
@ -140,13 +153,12 @@ async def async_get_config_entry_diagnostics(
|
||||||
|
|
||||||
# Find all routes currently act that might be thread related, so we can match them to
|
# Find all routes currently act that might be thread related, so we can match them to
|
||||||
# border routers as we process the zeroconf data.
|
# border routers as we process the zeroconf data.
|
||||||
routes, reverse_routes = await hass.async_add_executor_job(
|
#
|
||||||
_get_possible_thread_routes
|
# Also find all neighbours
|
||||||
|
routes, reverse_routes, neighbours = await hass.async_add_executor_job(
|
||||||
|
_get_routes_and_neighbors
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find all neighbours
|
|
||||||
neighbours = await hass.async_add_executor_job(_get_neighbours)
|
|
||||||
|
|
||||||
aiozc = await zeroconf.async_get_async_instance(hass)
|
aiozc = await zeroconf.async_get_async_instance(hass)
|
||||||
for data in async_read_zeroconf_cache(aiozc):
|
for data in async_read_zeroconf_cache(aiozc):
|
||||||
if not data.extended_pan_id:
|
if not data.extended_pan_id:
|
||||||
|
|
|
@ -133,9 +133,7 @@ class MockNeighbour:
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ndb() -> Mock:
|
def ndb() -> Mock:
|
||||||
"""Prevent NDB poking the OS route tables."""
|
"""Prevent NDB poking the OS route tables."""
|
||||||
with patch(
|
with patch("pyroute2.NDB") as ndb, ndb() as instance:
|
||||||
"homeassistant.components.thread.diagnostics.NDB"
|
|
||||||
) as ndb, ndb() as instance:
|
|
||||||
instance.neighbours = []
|
instance.neighbours = []
|
||||||
instance.routes = []
|
instance.routes = []
|
||||||
yield instance
|
yield instance
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue