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:
Pascal Vizeli 2020-11-13 12:19:10 +01:00 committed by GitHub
parent 06ab7e904f
commit 5552b1da49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 164 additions and 20 deletions

View File

@ -41,7 +41,7 @@ setup(
"supervisor.docker",
"supervisor.homeassistant",
"supervisor.host",
"supervisor.job",
"supervisor.jobs",
"supervisor.misc",
"supervisor.plugins",
"supervisor.resolution.evaluations",

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
[4]

View File

@ -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()

View 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