mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-27 02:56:31 +00:00
Slowdown snapshots to make it faster on slow IO (#1803)
* Slowdown snapshots to make it faster on slow IO * Fix error handling * fix lint
This commit is contained in:
parent
5a3ebaf683
commit
7c5f710deb
@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL, CoreStates
|
from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL, CoreStates
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
from ..exceptions import AddonsError
|
||||||
from ..utils.dt import utcnow
|
from ..utils.dt import utcnow
|
||||||
from .snapshot import Snapshot
|
from .snapshot import Snapshot
|
||||||
from .utils import create_slug
|
from .utils import create_slug
|
||||||
@ -219,8 +220,6 @@ class SnapshotManager(CoreSysAttributes):
|
|||||||
await self.lock.acquire()
|
await self.lock.acquire()
|
||||||
|
|
||||||
async with snapshot:
|
async with snapshot:
|
||||||
tasks = []
|
|
||||||
|
|
||||||
# Stop Home-Assistant / Add-ons
|
# Stop Home-Assistant / Add-ons
|
||||||
await self.sys_core.shutdown()
|
await self.sys_core.shutdown()
|
||||||
|
|
||||||
@ -240,14 +239,17 @@ class SnapshotManager(CoreSysAttributes):
|
|||||||
await snapshot.restore_repositories()
|
await snapshot.restore_repositories()
|
||||||
|
|
||||||
# Delete delta add-ons
|
# Delete delta add-ons
|
||||||
tasks.clear()
|
_LOGGER.info("Restore %s remove add-ons", snapshot.slug)
|
||||||
for addon in self.sys_addons.installed:
|
for addon in self.sys_addons.installed:
|
||||||
if addon.slug not in snapshot.addon_list:
|
if addon.slug in snapshot.addon_list:
|
||||||
tasks.append(addon.uninstall())
|
continue
|
||||||
|
|
||||||
if tasks:
|
# Remove Add-on because it's not a part of the new env
|
||||||
_LOGGER.info("Restore %s remove add-ons", snapshot.slug)
|
# Do it sequential avoid issue on slow IO
|
||||||
await asyncio.wait(tasks)
|
try:
|
||||||
|
await addon.uninstall()
|
||||||
|
except AddonsError:
|
||||||
|
_LOGGER.warning("Can't uninstall Add-on %s", addon.slug)
|
||||||
|
|
||||||
# Restore add-ons
|
# Restore add-ons
|
||||||
_LOGGER.info("Restore %s old add-ons", snapshot.slug)
|
_LOGGER.info("Restore %s old add-ons", snapshot.slug)
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
"""Representation of a snapshot file."""
|
"""Representation of a snapshot file."""
|
||||||
import asyncio
|
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import tarfile
|
import tarfile
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import padding
|
from cryptography.hazmat.primitives import padding
|
||||||
@ -14,6 +13,7 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
|
from ..addons import Addon
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ATTR_ADDONS,
|
ATTR_ADDONS,
|
||||||
ATTR_AUDIO_INPUT,
|
ATTR_AUDIO_INPUT,
|
||||||
@ -42,11 +42,7 @@ from ..const import (
|
|||||||
from ..coresys import CoreSys, CoreSysAttributes
|
from ..coresys import CoreSys, CoreSysAttributes
|
||||||
from ..exceptions import AddonsError
|
from ..exceptions import AddonsError
|
||||||
from ..utils.json import write_json_file
|
from ..utils.json import write_json_file
|
||||||
from ..utils.tar import (
|
from ..utils.tar import SecureTarFile, atomic_contents_add, secure_path
|
||||||
SecureTarFile,
|
|
||||||
secure_path,
|
|
||||||
atomic_contents_add,
|
|
||||||
)
|
|
||||||
from .utils import key_to_iv, password_for_validating, password_to_key, remove_folder
|
from .utils import key_to_iv, password_for_validating, password_to_key, remove_folder
|
||||||
from .validate import ALL_FOLDERS, SCHEMA_SNAPSHOT
|
from .validate import ALL_FOLDERS, SCHEMA_SNAPSHOT
|
||||||
|
|
||||||
@ -297,11 +293,11 @@ class Snapshot(CoreSysAttributes):
|
|||||||
finally:
|
finally:
|
||||||
self._tmp.cleanup()
|
self._tmp.cleanup()
|
||||||
|
|
||||||
async def store_addons(self, addon_list=None):
|
async def store_addons(self, addon_list: Optional[List[Addon]] = None):
|
||||||
"""Add a list of add-ons into snapshot."""
|
"""Add a list of add-ons into snapshot."""
|
||||||
addon_list = addon_list or self.sys_addons.installed
|
addon_list: List[Addon] = addon_list or self.sys_addons.installed
|
||||||
|
|
||||||
async def _addon_save(addon):
|
async def _addon_save(addon: Addon):
|
||||||
"""Task to store an add-on into snapshot."""
|
"""Task to store an add-on into snapshot."""
|
||||||
addon_file = SecureTarFile(
|
addon_file = SecureTarFile(
|
||||||
Path(self._tmp.name, f"{addon.slug}.tar.gz"), "w", key=self._key
|
Path(self._tmp.name, f"{addon.slug}.tar.gz"), "w", key=self._key
|
||||||
@ -324,16 +320,19 @@ class Snapshot(CoreSysAttributes):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run tasks
|
# Save Add-ons sequential
|
||||||
tasks = [_addon_save(addon) for addon in addon_list]
|
# avoid issue on slow IO
|
||||||
if tasks:
|
for addon in addon_list:
|
||||||
await asyncio.wait(tasks)
|
try:
|
||||||
|
await _addon_save(addon)
|
||||||
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
_LOGGER.warning("Can't save Add-on %s: %s", addon.slug, err)
|
||||||
|
|
||||||
async def restore_addons(self, addon_list=None):
|
async def restore_addons(self, addon_list: Optional[List[str]] = None):
|
||||||
"""Restore a list add-on from snapshot."""
|
"""Restore a list add-on from snapshot."""
|
||||||
addon_list = addon_list or self.addon_list
|
addon_list: List[str] = addon_list or self.addon_list
|
||||||
|
|
||||||
async def _addon_restore(addon_slug):
|
async def _addon_restore(addon_slug: str):
|
||||||
"""Task to restore an add-on into snapshot."""
|
"""Task to restore an add-on into snapshot."""
|
||||||
addon_file = SecureTarFile(
|
addon_file = SecureTarFile(
|
||||||
Path(self._tmp.name, f"{addon_slug}.tar.gz"), "r", key=self._key
|
Path(self._tmp.name, f"{addon_slug}.tar.gz"), "r", key=self._key
|
||||||
@ -350,16 +349,19 @@ class Snapshot(CoreSysAttributes):
|
|||||||
except AddonsError:
|
except AddonsError:
|
||||||
_LOGGER.error("Can't restore snapshot for %s", addon_slug)
|
_LOGGER.error("Can't restore snapshot for %s", addon_slug)
|
||||||
|
|
||||||
# Run tasks
|
# Save Add-ons sequential
|
||||||
tasks = [_addon_restore(slug) for slug in addon_list]
|
# avoid issue on slow IO
|
||||||
if tasks:
|
for slug in addon_list:
|
||||||
await asyncio.wait(tasks)
|
try:
|
||||||
|
await _addon_restore(slug)
|
||||||
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
_LOGGER.warning("Can't restore Add-on %s: %s", slug, err)
|
||||||
|
|
||||||
async def store_folders(self, folder_list=None):
|
async def store_folders(self, folder_list: Optional[List[str]] = None):
|
||||||
"""Backup Supervisor data into snapshot."""
|
"""Backup Supervisor data into snapshot."""
|
||||||
folder_list = set(folder_list or ALL_FOLDERS)
|
folder_list: Set[str] = set(folder_list or ALL_FOLDERS)
|
||||||
|
|
||||||
def _folder_save(name):
|
def _folder_save(name: str):
|
||||||
"""Take snapshot of a folder."""
|
"""Take snapshot of a folder."""
|
||||||
slug_name = name.replace("/", "_")
|
slug_name = name.replace("/", "_")
|
||||||
tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz")
|
tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz")
|
||||||
@ -386,18 +388,19 @@ class Snapshot(CoreSysAttributes):
|
|||||||
except (tarfile.TarError, OSError) as err:
|
except (tarfile.TarError, OSError) as err:
|
||||||
_LOGGER.warning("Can't snapshot folder %s: %s", name, err)
|
_LOGGER.warning("Can't snapshot folder %s: %s", name, err)
|
||||||
|
|
||||||
# Run tasks
|
# Save folder sequential
|
||||||
tasks = [
|
# avoid issue on slow IO
|
||||||
self.sys_run_in_executor(_folder_save, folder) for folder in folder_list
|
for folder in folder_list:
|
||||||
]
|
try:
|
||||||
if tasks:
|
await self.sys_run_in_executor(_folder_save, folder)
|
||||||
await asyncio.wait(tasks)
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
_LOGGER.warning("Can't save folder %s: %s", folder, err)
|
||||||
|
|
||||||
async def restore_folders(self, folder_list=None):
|
async def restore_folders(self, folder_list: Optional[List[str]] = None):
|
||||||
"""Backup Supervisor data into snapshot."""
|
"""Backup Supervisor data into snapshot."""
|
||||||
folder_list = set(folder_list or self.folders)
|
folder_list: Set[str] = set(folder_list or self.folders)
|
||||||
|
|
||||||
def _folder_restore(name):
|
def _folder_restore(name: str):
|
||||||
"""Intenal function to restore a folder."""
|
"""Intenal function to restore a folder."""
|
||||||
slug_name = name.replace("/", "_")
|
slug_name = name.replace("/", "_")
|
||||||
tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz")
|
tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz")
|
||||||
@ -421,12 +424,13 @@ class Snapshot(CoreSysAttributes):
|
|||||||
except (tarfile.TarError, OSError) as err:
|
except (tarfile.TarError, OSError) as err:
|
||||||
_LOGGER.warning("Can't restore folder %s: %s", name, err)
|
_LOGGER.warning("Can't restore folder %s: %s", name, err)
|
||||||
|
|
||||||
# Run tasks
|
# Restore folder sequential
|
||||||
tasks = [
|
# avoid issue on slow IO
|
||||||
self.sys_run_in_executor(_folder_restore, folder) for folder in folder_list
|
for folder in folder_list:
|
||||||
]
|
try:
|
||||||
if tasks:
|
await self.sys_run_in_executor(_folder_restore, folder)
|
||||||
await asyncio.wait(tasks)
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
_LOGGER.warning("Can't restore folder %s: %s", folder, err)
|
||||||
|
|
||||||
def store_homeassistant(self):
|
def store_homeassistant(self):
|
||||||
"""Read all data from Home Assistant object."""
|
"""Read all data from Home Assistant object."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user