Fix watchdog & scheduler (#1757)

* Fix watchdog & scheduler

* Update supervisor/misc/scheduler.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Fix callback

* hmm

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Pascal Vizeli 2020-05-28 14:25:36 +02:00 committed by GitHub
parent 44fa34203a
commit 7bd6ff374a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 51 additions and 33 deletions

View File

@ -27,15 +27,16 @@ from .discovery import Discovery
from .hassos import HassOS from .hassos import HassOS
from .homeassistant import HomeAssistant from .homeassistant import HomeAssistant
from .host import HostManager from .host import HostManager
from .hwmon import HwMonitor
from .ingress import Ingress from .ingress import Ingress
from .misc.hwmon import HwMonitor
from .misc.scheduler import Scheduler
from .misc.secrets import SecretsManager
from .misc.tasks import Tasks
from .plugins import PluginManager from .plugins import PluginManager
from .secrets import SecretsManager
from .services import ServiceManager from .services import ServiceManager
from .snapshots import SnapshotManager from .snapshots import SnapshotManager
from .store import StoreManager from .store import StoreManager
from .supervisor import Supervisor from .supervisor import Supervisor
from .tasks import Tasks
from .updater import Updater from .updater import Updater
from .utils.dt import fetch_timezone from .utils.dt import fetch_timezone
@ -70,6 +71,7 @@ async def initialize_coresys() -> None:
coresys.dbus = DBusManager(coresys) coresys.dbus = DBusManager(coresys)
coresys.hassos = HassOS(coresys) coresys.hassos = HassOS(coresys)
coresys.secrets = SecretsManager(coresys) coresys.secrets = SecretsManager(coresys)
coresys.scheduler = Scheduler(coresys)
# bootstrap config # bootstrap config
initialize_system_data(coresys) initialize_system_data(coresys)

View File

@ -362,6 +362,7 @@ class CoreStates(str, Enum):
STARTUP = "startup" STARTUP = "startup"
RUNNING = "running" RUNNING = "running"
FREEZE = "freeze" FREEZE = "freeze"
STOPPING = "stopping"
class LogLevel(str, Enum): class LogLevel(str, Enum):

View File

@ -187,7 +187,7 @@ class Core(CoreSysAttributes):
async def stop(self): async def stop(self):
"""Stop a running orchestration.""" """Stop a running orchestration."""
# don't process scheduler anymore # don't process scheduler anymore
self.sys_scheduler.suspend = True self.state = CoreStates.STOPPING
# store new last boot / prevent time adjustments # store new last boot / prevent time adjustments
if self.state == CoreStates.RUNNING: if self.state == CoreStates.RUNNING:
@ -213,12 +213,17 @@ class Core(CoreSysAttributes):
async def shutdown(self): async def shutdown(self):
"""Shutdown all running containers in correct order.""" """Shutdown all running containers in correct order."""
# don't process scheduler anymore
self.state = CoreStates.STOPPING
# Shutdown Application Add-ons, using Home Assistant API
await self.sys_addons.shutdown(STARTUP_APPLICATION) await self.sys_addons.shutdown(STARTUP_APPLICATION)
# Close Home Assistant # Close Home Assistant
with suppress(HassioError): with suppress(HassioError):
await self.sys_homeassistant.stop() await self.sys_homeassistant.stop()
# Shutdown System Add-ons
await self.sys_addons.shutdown(STARTUP_SERVICES) await self.sys_addons.shutdown(STARTUP_SERVICES)
await self.sys_addons.shutdown(STARTUP_SYSTEM) await self.sys_addons.shutdown(STARTUP_SYSTEM)
await self.sys_addons.shutdown(STARTUP_INITIALIZE) await self.sys_addons.shutdown(STARTUP_INITIALIZE)

View File

@ -10,7 +10,6 @@ from .config import CoreConfig
from .const import UpdateChannels from .const import UpdateChannels
from .docker import DockerAPI from .docker import DockerAPI
from .misc.hardware import Hardware from .misc.hardware import Hardware
from .misc.scheduler import Scheduler
if TYPE_CHECKING: if TYPE_CHECKING:
from .addons import AddonManager from .addons import AddonManager
@ -21,16 +20,17 @@ if TYPE_CHECKING:
from .dbus import DBusManager from .dbus import DBusManager
from .discovery import Discovery from .discovery import Discovery
from .hassos import HassOS from .hassos import HassOS
from .hwmon import HwMonitor from .misc.scheduler import Scheduler
from .misc.hwmon import HwMonitor
from .misc.secrets import SecretsManager
from .misc.tasks import Tasks
from .homeassistant import HomeAssistant from .homeassistant import HomeAssistant
from .host import HostManager from .host import HostManager
from .ingress import Ingress from .ingress import Ingress
from .secrets import SecretsManager
from .services import ServiceManager from .services import ServiceManager
from .snapshots import SnapshotManager from .snapshots import SnapshotManager
from .supervisor import Supervisor from .supervisor import Supervisor
from .store import StoreManager from .store import StoreManager
from .tasks import Tasks
from .updater import Updater from .updater import Updater
from .plugins import PluginManager from .plugins import PluginManager
@ -58,7 +58,6 @@ class CoreSys:
self._config: CoreConfig = CoreConfig() self._config: CoreConfig = CoreConfig()
self._hardware: Hardware = Hardware() self._hardware: Hardware = Hardware()
self._docker: DockerAPI = DockerAPI() self._docker: DockerAPI = DockerAPI()
self._scheduler: Scheduler = Scheduler()
# Internal objects pointers # Internal objects pointers
self._core: Optional[Core] = None self._core: Optional[Core] = None
@ -77,6 +76,7 @@ class CoreSys:
self._hassos: Optional[HassOS] = None self._hassos: Optional[HassOS] = None
self._services: Optional[ServiceManager] = None self._services: Optional[ServiceManager] = None
self._secrets: Optional[SecretsManager] = None self._secrets: Optional[SecretsManager] = None
self._scheduler: Optional[Scheduler] = None
self._store: Optional[StoreManager] = None self._store: Optional[StoreManager] = None
self._discovery: Optional[Discovery] = None self._discovery: Optional[Discovery] = None
self._hwmonitor: Optional[HwMonitor] = None self._hwmonitor: Optional[HwMonitor] = None
@ -127,8 +127,17 @@ class CoreSys:
@property @property
def scheduler(self) -> Scheduler: def scheduler(self) -> Scheduler:
"""Return Scheduler object.""" """Return Scheduler object."""
if self._scheduler is None:
raise RuntimeError("Scheduler not set!")
return self._scheduler return self._scheduler
@scheduler.setter
def scheduler(self, value: Scheduler) -> None:
"""Set a Scheduler object."""
if self._scheduler:
raise RuntimeError("Scheduler already set!")
self._scheduler = value
@property @property
def core(self) -> Core: def core(self) -> Core:
"""Return core object.""" """Return core object."""

View File

@ -6,8 +6,8 @@ from typing import Optional
import pyudev import pyudev
from .coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from .utils import AsyncCallFilter from ..utils import AsyncCallFilter
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -1,8 +1,10 @@
"""Schedule for Supervisor.""" """Schedule for Supervisor."""
import asyncio
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
import logging import logging
from ..const import CoreStates
from ..coresys import CoreSys, CoreSysAttributes
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
INTERVAL = "interval" INTERVAL = "interval"
@ -11,14 +13,13 @@ CALL = "callback"
TASK = "task" TASK = "task"
class Scheduler: class Scheduler(CoreSysAttributes):
"""Schedule task inside Supervisor.""" """Schedule task inside Supervisor."""
def __init__(self): def __init__(self, coresys: CoreSys):
"""Initialize task schedule.""" """Initialize task schedule."""
self.loop = asyncio.get_running_loop() self.coresys: CoreSys = coresys
self._data = {} self._data = {}
self.suspend = False
def register_task(self, coro_callback, interval, repeat=True): def register_task(self, coro_callback, interval, repeat=True):
"""Schedule a coroutine. """Schedule a coroutine.
@ -40,8 +41,8 @@ class Scheduler:
"""Run a scheduled task.""" """Run a scheduled task."""
data = self._data[task_id] data = self._data[task_id]
if not self.suspend: if self.sys_core.state == CoreStates.RUNNING:
self.loop.create_task(data[CALL]()) self.sys_create_task(data[CALL]())
if data[REPEAT]: if data[REPEAT]:
self._schedule_task(data[INTERVAL], task_id) self._schedule_task(data[INTERVAL], task_id)
@ -51,7 +52,7 @@ class Scheduler:
def _schedule_task(self, interval, task_id): def _schedule_task(self, interval, task_id):
"""Schedule a task on loop.""" """Schedule a task on loop."""
if isinstance(interval, (int, float)): if isinstance(interval, (int, float)):
job = self.loop.call_later(interval, self._run_task, task_id) job = self.sys_loop.call_later(interval, self._run_task, task_id)
elif isinstance(interval, time): elif isinstance(interval, time):
today = datetime.combine(date.today(), interval) today = datetime.combine(date.today(), interval)
tomorrow = datetime.combine(date.today() + timedelta(days=1), interval) tomorrow = datetime.combine(date.today() + timedelta(days=1), interval)
@ -62,7 +63,7 @@ class Scheduler:
else: else:
calc = tomorrow calc = tomorrow
job = self.loop.call_at(calc.timestamp(), self._run_task, task_id) job = self.sys_loop.call_at(calc.timestamp(), self._run_task, task_id)
else: else:
_LOGGER.critical( _LOGGER.critical(
"Unknown interval %s (type: %s) for scheduler %s", "Unknown interval %s (type: %s) for scheduler %s",

View File

@ -6,8 +6,8 @@ from typing import Dict, Optional, Union
from ruamel.yaml import YAML, YAMLError from ruamel.yaml import YAML, YAMLError
from .coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from .utils import AsyncThrottle from ..utils import AsyncThrottle
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -2,8 +2,8 @@
import asyncio import asyncio
import logging import logging
from .coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from .exceptions import ( from ..exceptions import (
AudioError, AudioError,
CliError, CliError,
CoreDNSError, CoreDNSError,

View File

@ -3,7 +3,7 @@ import asyncio
import logging import logging
from pathlib import Path from pathlib import Path
from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL, CoreStates
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..utils.dt import utcnow from ..utils.dt import utcnow
from .snapshot import Snapshot from .snapshot import Snapshot
@ -125,7 +125,7 @@ class SnapshotManager(CoreSysAttributes):
snapshot = self._create_snapshot(name, SNAPSHOT_FULL, password) snapshot = self._create_snapshot(name, SNAPSHOT_FULL, password)
_LOGGER.info("Full-Snapshot %s start", snapshot.slug) _LOGGER.info("Full-Snapshot %s start", snapshot.slug)
try: try:
self.sys_scheduler.suspend = True self.sys_core.state = CoreStates.FREEZE
await self.lock.acquire() await self.lock.acquire()
async with snapshot: async with snapshot:
@ -147,7 +147,7 @@ class SnapshotManager(CoreSysAttributes):
return snapshot return snapshot
finally: finally:
self.sys_scheduler.suspend = False self.sys_core.state = CoreStates.RUNNING
self.lock.release() self.lock.release()
async def do_snapshot_partial( async def do_snapshot_partial(
@ -164,7 +164,7 @@ class SnapshotManager(CoreSysAttributes):
_LOGGER.info("Partial-Snapshot %s start", snapshot.slug) _LOGGER.info("Partial-Snapshot %s start", snapshot.slug)
try: try:
self.sys_scheduler.suspend = True self.sys_core.state = CoreStates.FREEZE
await self.lock.acquire() await self.lock.acquire()
async with snapshot: async with snapshot:
@ -196,7 +196,7 @@ class SnapshotManager(CoreSysAttributes):
return snapshot return snapshot
finally: finally:
self.sys_scheduler.suspend = False self.sys_core.state = CoreStates.RUNNING
self.lock.release() self.lock.release()
async def do_restore_full(self, snapshot, password=None): async def do_restore_full(self, snapshot, password=None):
@ -215,7 +215,7 @@ class SnapshotManager(CoreSysAttributes):
_LOGGER.info("Full-Restore %s start", snapshot.slug) _LOGGER.info("Full-Restore %s start", snapshot.slug)
try: try:
self.sys_scheduler.suspend = True self.sys_core.state = CoreStates.FREEZE
await self.lock.acquire() await self.lock.acquire()
async with snapshot: async with snapshot:
@ -267,7 +267,7 @@ class SnapshotManager(CoreSysAttributes):
return True return True
finally: finally:
self.sys_scheduler.suspend = False self.sys_core.state = CoreStates.RUNNING
self.lock.release() self.lock.release()
async def do_restore_partial( async def do_restore_partial(
@ -287,7 +287,7 @@ class SnapshotManager(CoreSysAttributes):
_LOGGER.info("Partial-Restore %s start", snapshot.slug) _LOGGER.info("Partial-Restore %s start", snapshot.slug)
try: try:
self.sys_scheduler.suspend = True self.sys_core.state = CoreStates.FREEZE
await self.lock.acquire() await self.lock.acquire()
async with snapshot: async with snapshot:
@ -339,5 +339,5 @@ class SnapshotManager(CoreSysAttributes):
return True return True
finally: finally:
self.sys_scheduler.suspend = False self.sys_core.state = CoreStates.RUNNING
self.lock.release() self.lock.release()