mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 13:46:31 +00:00
Optimize flow / reduce call time (#2250)
* Optimize flow / reduce call time * rename * freeze too * add connectivity task * use newstyle timeout * Fix tests Co-authored-by: Ludeeus <ludeeus@ludeeus.dev>
This commit is contained in:
parent
06ab7e904f
commit
5552b1da49
2
setup.py
2
setup.py
@ -41,7 +41,7 @@ setup(
|
||||
"supervisor.docker",
|
||||
"supervisor.homeassistant",
|
||||
"supervisor.host",
|
||||
"supervisor.job",
|
||||
"supervisor.jobs",
|
||||
"supervisor.misc",
|
||||
"supervisor.plugins",
|
||||
"supervisor.resolution.evaluations",
|
||||
|
@ -10,7 +10,7 @@ import sentry_sdk
|
||||
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
|
||||
from supervisor.job import JobManager
|
||||
from supervisor.jobs import JobManager
|
||||
|
||||
from .addons import AddonManager
|
||||
from .api import RestAPI
|
||||
|
@ -57,9 +57,6 @@ class Core(CoreSysAttributes):
|
||||
# Load information from container
|
||||
await self.sys_supervisor.load()
|
||||
|
||||
# Check internet on startup
|
||||
await self.sys_supervisor.check_connectivity()
|
||||
|
||||
# Evaluate the system
|
||||
await self.sys_resolution.evaluate.evaluate_system()
|
||||
|
||||
@ -140,6 +137,9 @@ class Core(CoreSysAttributes):
|
||||
"System running in a unhealthy state and need manual intervention!"
|
||||
)
|
||||
|
||||
# Check internet on startup
|
||||
await self.sys_supervisor.check_connectivity()
|
||||
|
||||
# Mark booted partition as healthy
|
||||
if self.sys_hassos.available:
|
||||
await self.sys_hassos.mark_healthy()
|
||||
|
@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
||||
from .homeassistant import HomeAssistant
|
||||
from .host import HostManager
|
||||
from .ingress import Ingress
|
||||
from .job import JobManager
|
||||
from .jobs import JobManager
|
||||
from .misc.hwmon import HwMonitor
|
||||
from .misc.scheduler import Scheduler
|
||||
from .misc.tasks import Tasks
|
||||
|
@ -75,7 +75,8 @@ class NetworkManager(CoreSysAttributes):
|
||||
try:
|
||||
state = await self.sys_dbus.network.check_connectivity()
|
||||
self._connectivity = state[0] == 4
|
||||
except DBusError:
|
||||
except DBusError as err:
|
||||
_LOGGER.warning("Can't update connectivity information: %s", err)
|
||||
self._connectivity = False
|
||||
|
||||
def get(self, inet_name: str) -> Interface:
|
||||
@ -95,9 +96,11 @@ class NetworkManager(CoreSysAttributes):
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't update network information!")
|
||||
except DBusNotConnectedError as err:
|
||||
_LOGGER.error("No hostname D-Bus connection available")
|
||||
_LOGGER.error("No network D-Bus connection available")
|
||||
raise HostNotSupportedError() from err
|
||||
|
||||
await self.check_connectivity()
|
||||
|
||||
async def apply_changes(self, interface: Interface) -> None:
|
||||
"""Apply Interface changes to host."""
|
||||
inet = self.sys_dbus.network.interfaces.get(interface.name)
|
||||
|
@ -53,7 +53,7 @@ class Job:
|
||||
|
||||
job = self._coresys.jobs.get_job(self.name)
|
||||
|
||||
if self.conditions and not await self._check_conditions():
|
||||
if self.conditions and not self._check_conditions():
|
||||
return False
|
||||
|
||||
try:
|
||||
@ -69,7 +69,7 @@ class Job:
|
||||
|
||||
return wrapper
|
||||
|
||||
async def _check_conditions(self):
|
||||
def _check_conditions(self):
|
||||
"""Check conditions."""
|
||||
if JobCondition.HEALTHY in self.conditions:
|
||||
if not self._coresys.core.healthy:
|
||||
@ -93,10 +93,12 @@ class Job:
|
||||
return False
|
||||
|
||||
if JobCondition.INTERNET in self.conditions:
|
||||
if self._coresys.core.state == CoreState.RUNNING:
|
||||
if self._coresys.dbus.network.is_connected:
|
||||
await self._coresys.host.network.check_connectivity()
|
||||
await self._coresys.supervisor.check_connectivity()
|
||||
if self._coresys.core.state not in (
|
||||
CoreState.SETUP,
|
||||
CoreState.RUNNING,
|
||||
):
|
||||
return True
|
||||
|
||||
if not self._coresys.supervisor.connectivity:
|
||||
_LOGGER.warning(
|
||||
"'%s' blocked from execution, no supervisor internet connection",
|
@ -12,7 +12,7 @@ from ..exceptions import (
|
||||
MulticastError,
|
||||
ObserverError,
|
||||
)
|
||||
from ..job.decorator import Job, JobCondition
|
||||
from ..jobs.decorator import Job, JobCondition
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
@ -47,6 +47,8 @@ RUN_WATCHDOG_OBSERVER_APPLICATION = 180
|
||||
|
||||
RUN_REFRESH_ADDON = 15
|
||||
|
||||
RUN_CHECK_CONNECTIVITY = 30
|
||||
|
||||
|
||||
class Tasks(CoreSysAttributes):
|
||||
"""Handle Tasks inside Supervisor."""
|
||||
@ -111,6 +113,11 @@ class Tasks(CoreSysAttributes):
|
||||
# Refresh
|
||||
self.sys_scheduler.register_task(self._refresh_addon, RUN_REFRESH_ADDON)
|
||||
|
||||
# Connectivity
|
||||
self.sys_scheduler.register_task(
|
||||
self._check_connectivity, RUN_CHECK_CONNECTIVITY
|
||||
)
|
||||
|
||||
_LOGGER.info("All core tasks are scheduled")
|
||||
|
||||
@Job(conditions=[JobCondition.HEALTHY, JobCondition.FREE_SPACE])
|
||||
@ -422,3 +429,29 @@ class Tasks(CoreSysAttributes):
|
||||
|
||||
# Adjust state
|
||||
addon.state = AddonState.STOPPED
|
||||
|
||||
async def _check_connectivity(self) -> None:
|
||||
"""Check system connectivity."""
|
||||
value = self._cache.get("connectivity", 0)
|
||||
|
||||
# Need only full check if not connected or each 10min
|
||||
if value >= 600:
|
||||
pass
|
||||
elif (
|
||||
self.sys_supervisor.connectivity
|
||||
and self.sys_host.network.connectivity is None
|
||||
) or (
|
||||
self.sys_supervisor.connectivity
|
||||
and self.sys_host.network.connectivity is not None
|
||||
and self.sys_host.network.connectivity
|
||||
):
|
||||
self._cache["connectivity"] = value + RUN_CHECK_CONNECTIVITY
|
||||
return
|
||||
|
||||
# Check connectivity
|
||||
try:
|
||||
await self.sys_supervisor.check_connectivity()
|
||||
if self.sys_dbus.network.is_connected:
|
||||
await self.sys_host.network.check_connectivity()
|
||||
finally:
|
||||
self._cache["connectivity"] = 0
|
||||
|
@ -12,7 +12,7 @@ from supervisor.utils.json import read_json_file
|
||||
from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from ..exceptions import JsonFileError, StoreError, StoreGitError
|
||||
from ..job.decorator import Job, JobCondition
|
||||
from ..jobs.decorator import Job, JobCondition
|
||||
from .addon import AddonStore
|
||||
from .data import StoreData
|
||||
from .repository import Repository
|
||||
|
@ -33,7 +33,7 @@ class Supervisor(CoreSysAttributes):
|
||||
"""Initialize hass object."""
|
||||
self.coresys: CoreSys = coresys
|
||||
self.instance: DockerSupervisor = DockerSupervisor(coresys)
|
||||
self._connectivity: bool = False
|
||||
self._connectivity: bool = True
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Prepare Home Assistant object."""
|
||||
@ -176,9 +176,10 @@ class Supervisor(CoreSysAttributes):
|
||||
|
||||
async def check_connectivity(self):
|
||||
"""Check the connection."""
|
||||
timeout = aiohttp.ClientTimeout(total=10)
|
||||
try:
|
||||
await self.sys_websession.head(
|
||||
"https://version.home-assistant.io/online.txt", timeout=10
|
||||
"https://version.home-assistant.io/online.txt", timeout=timeout
|
||||
)
|
||||
except (ClientError, asyncio.TimeoutError):
|
||||
self._connectivity = False
|
||||
|
@ -25,7 +25,7 @@ from .const import (
|
||||
)
|
||||
from .coresys import CoreSysAttributes
|
||||
from .exceptions import HassioUpdaterError
|
||||
from .job.decorator import Job, JobCondition
|
||||
from .jobs.decorator import Job, JobCondition
|
||||
from .utils import AsyncThrottle
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_UPDATER_CONFIG
|
||||
|
1
tests/fixtures/org_freedesktop_NetworkManager-CheckConnectivity.fixture
vendored
Normal file
1
tests/fixtures/org_freedesktop_NetworkManager-CheckConnectivity.fixture
vendored
Normal file
@ -0,0 +1 @@
|
||||
[4]
|
@ -2,8 +2,9 @@
|
||||
# pylint: disable=protected-access,import-error
|
||||
from unittest.mock import patch
|
||||
|
||||
from supervisor.const import CoreState
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.job.decorator import Job, JobCondition
|
||||
from supervisor.jobs.decorator import Job, JobCondition
|
||||
|
||||
|
||||
async def test_healthy(coresys: CoreSys):
|
||||
@ -30,6 +31,7 @@ async def test_healthy(coresys: CoreSys):
|
||||
|
||||
async def test_internet(coresys: CoreSys):
|
||||
"""Test the internet decorator."""
|
||||
coresys.core.state = CoreState.RUNNING
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
@ -83,3 +85,44 @@ async def test_free_space(coresys: CoreSys):
|
||||
|
||||
with patch("shutil.disk_usage", return_value=(42, 42, (512.0 ** 3))):
|
||||
assert not await test.execute()
|
||||
|
||||
|
||||
async def test_internet_connectivity_with_core_state(coresys: CoreSys):
|
||||
"""Test the different core states and the impact for internet condition."""
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize the test class."""
|
||||
self.coresys = coresys
|
||||
|
||||
@Job(conditions=[JobCondition.INTERNET])
|
||||
async def execute(self):
|
||||
"""Execute the class method."""
|
||||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
coresys.host.network._connectivity = False
|
||||
coresys.supervisor._connectivity = False
|
||||
|
||||
coresys.core.state = CoreState.INITIALIZE
|
||||
assert await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.SETUP
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.STARTUP
|
||||
assert await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.RUNNING
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.CLOSE
|
||||
assert await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.SHUTDOWN
|
||||
assert await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.STOPPING
|
||||
assert await test.execute()
|
||||
|
61
tests/misc/test_connectivity_task.py
Normal file
61
tests/misc/test_connectivity_task.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Test periodic connectivity task."""
|
||||
# pylint: disable=protected-access,import-error
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from supervisor.coresys import CoreSys
|
||||
|
||||
|
||||
async def test_no_connectivity(coresys: CoreSys):
|
||||
"""Test periodic connectivity task."""
|
||||
coresys.host.network.check_connectivity = AsyncMock()
|
||||
coresys.supervisor.check_connectivity = AsyncMock()
|
||||
|
||||
coresys.tasks._cache["connectivity"] = 0
|
||||
coresys.host.network._connectivity = False
|
||||
coresys.supervisor._connectivity = False
|
||||
|
||||
await coresys.tasks._check_connectivity()
|
||||
|
||||
coresys.host.network.check_connectivity.assert_called_once()
|
||||
coresys.supervisor.check_connectivity.assert_called_once()
|
||||
assert coresys.tasks._cache["connectivity"] == 0
|
||||
coresys.host.network.check_connectivity.reset_mock()
|
||||
coresys.supervisor.check_connectivity.reset_mock()
|
||||
|
||||
await coresys.tasks._check_connectivity()
|
||||
|
||||
coresys.host.network.check_connectivity.assert_called_once()
|
||||
coresys.supervisor.check_connectivity.assert_called_once()
|
||||
assert coresys.tasks._cache["connectivity"] == 0
|
||||
|
||||
|
||||
async def test_connectivity(coresys: CoreSys):
|
||||
"""Test periodic connectivity task."""
|
||||
coresys.host.network.check_connectivity = AsyncMock()
|
||||
coresys.supervisor.check_connectivity = AsyncMock()
|
||||
|
||||
coresys.tasks._cache["connectivity"] = 0
|
||||
coresys.host.network._connectivity = True
|
||||
coresys.supervisor._connectivity = True
|
||||
|
||||
await coresys.tasks._check_connectivity()
|
||||
|
||||
coresys.host.network.check_connectivity.assert_not_called()
|
||||
coresys.supervisor.check_connectivity.assert_not_called()
|
||||
assert coresys.tasks._cache["connectivity"] == 30
|
||||
|
||||
|
||||
async def test_connectivity_cache_reached(coresys: CoreSys):
|
||||
"""Test periodic connectivity task."""
|
||||
coresys.host.network.check_connectivity = AsyncMock()
|
||||
coresys.supervisor.check_connectivity = AsyncMock()
|
||||
|
||||
coresys.tasks._cache["connectivity"] = 600
|
||||
coresys.host.network._connectivity = True
|
||||
coresys.supervisor._connectivity = True
|
||||
|
||||
await coresys.tasks._check_connectivity()
|
||||
|
||||
coresys.host.network.check_connectivity.assert_called_once()
|
||||
coresys.supervisor.check_connectivity.assert_called_once()
|
||||
assert coresys.tasks._cache["connectivity"] == 0
|
Loading…
x
Reference in New Issue
Block a user