mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 13:46:31 +00:00
Check if backup is still available when accessing
When accessing a backup (e.g. to restore, download or delete it) check if the locations of the backup file are still available. If not, force a backup reload instead.
This commit is contained in:
parent
f02d67ee47
commit
b68d381c22
@ -143,11 +143,29 @@ SCHEMA_REMOVE = vol.Schema(
|
|||||||
class APIBackups(CoreSysAttributes):
|
class APIBackups(CoreSysAttributes):
|
||||||
"""Handle RESTful API for backups functions."""
|
"""Handle RESTful API for backups functions."""
|
||||||
|
|
||||||
def _extract_slug(self, request):
|
async def _get_backup_by_slug(self, request):
|
||||||
"""Return backup, throw an exception if it doesn't exist."""
|
"""Return backup, throw an exception if it doesn't exist."""
|
||||||
backup = self.sys_backups.get(request.match_info.get("slug"))
|
backup = self.sys_backups.get(request.match_info.get("slug"))
|
||||||
if not backup:
|
if not backup:
|
||||||
raise APINotFound("Backup does not exist")
|
raise APINotFound("Backup does not exist")
|
||||||
|
|
||||||
|
# Reload in case locations of this backup are no longer in sync
|
||||||
|
# This usually means the user
|
||||||
|
def _check_need_reload():
|
||||||
|
for location in backup.all_locations:
|
||||||
|
if not Path(location).exists():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not await self.sys_run_in_executor(_check_need_reload):
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Backup %s missing in at least one location, scheduling reload",
|
||||||
|
backup.slug,
|
||||||
|
)
|
||||||
|
# Schedule a reload
|
||||||
|
self.sys_jobs.schedule_job(self.sys_backups.reload, JobSchedulerOptions())
|
||||||
|
raise APINotFound("Backup does not exist")
|
||||||
|
|
||||||
return backup
|
return backup
|
||||||
|
|
||||||
def _list_backups(self):
|
def _list_backups(self):
|
||||||
@ -212,7 +230,7 @@ class APIBackups(CoreSysAttributes):
|
|||||||
@api_process
|
@api_process
|
||||||
async def backup_info(self, request):
|
async def backup_info(self, request):
|
||||||
"""Return backup info."""
|
"""Return backup info."""
|
||||||
backup = self._extract_slug(request)
|
backup = await self._get_backup_by_slug(request)
|
||||||
|
|
||||||
data_addons = []
|
data_addons = []
|
||||||
for addon_data in backup.addons:
|
for addon_data in backup.addons:
|
||||||
@ -384,7 +402,7 @@ class APIBackups(CoreSysAttributes):
|
|||||||
@api_process
|
@api_process
|
||||||
async def restore_full(self, request: web.Request):
|
async def restore_full(self, request: web.Request):
|
||||||
"""Full restore of a backup."""
|
"""Full restore of a backup."""
|
||||||
backup = self._extract_slug(request)
|
backup = await self._get_backup_by_slug(request)
|
||||||
body = await api_validate(SCHEMA_RESTORE_FULL, request)
|
body = await api_validate(SCHEMA_RESTORE_FULL, request)
|
||||||
self._validate_cloud_backup_location(
|
self._validate_cloud_backup_location(
|
||||||
request, body.get(ATTR_LOCATION, backup.location)
|
request, body.get(ATTR_LOCATION, backup.location)
|
||||||
@ -404,7 +422,7 @@ class APIBackups(CoreSysAttributes):
|
|||||||
@api_process
|
@api_process
|
||||||
async def restore_partial(self, request: web.Request):
|
async def restore_partial(self, request: web.Request):
|
||||||
"""Partial restore a backup."""
|
"""Partial restore a backup."""
|
||||||
backup = self._extract_slug(request)
|
backup = await self._get_backup_by_slug(request)
|
||||||
body = await api_validate(SCHEMA_RESTORE_PARTIAL, request)
|
body = await api_validate(SCHEMA_RESTORE_PARTIAL, request)
|
||||||
self._validate_cloud_backup_location(
|
self._validate_cloud_backup_location(
|
||||||
request, body.get(ATTR_LOCATION, backup.location)
|
request, body.get(ATTR_LOCATION, backup.location)
|
||||||
@ -435,7 +453,7 @@ class APIBackups(CoreSysAttributes):
|
|||||||
@api_process
|
@api_process
|
||||||
async def remove(self, request: web.Request):
|
async def remove(self, request: web.Request):
|
||||||
"""Remove a backup."""
|
"""Remove a backup."""
|
||||||
backup = self._extract_slug(request)
|
backup = await self._get_backup_by_slug(request)
|
||||||
body = await api_validate(SCHEMA_REMOVE, request)
|
body = await api_validate(SCHEMA_REMOVE, request)
|
||||||
locations: list[LOCATION_TYPE] | None = None
|
locations: list[LOCATION_TYPE] | None = None
|
||||||
|
|
||||||
@ -450,7 +468,7 @@ class APIBackups(CoreSysAttributes):
|
|||||||
@api_process
|
@api_process
|
||||||
async def download(self, request: web.Request):
|
async def download(self, request: web.Request):
|
||||||
"""Download a backup file."""
|
"""Download a backup file."""
|
||||||
backup = self._extract_slug(request)
|
backup = self._get_backup_by_slug(request)
|
||||||
# Query will give us '' for /backups, convert value to None
|
# Query will give us '' for /backups, convert value to None
|
||||||
location = request.query.get(ATTR_LOCATION, backup.location) or None
|
location = request.query.get(ATTR_LOCATION, backup.location) or None
|
||||||
self._validate_cloud_backup_location(request, location)
|
self._validate_cloud_backup_location(request, location)
|
||||||
|
@ -200,18 +200,19 @@ class BackupManager(FileConfiguration, JobGroup):
|
|||||||
return backup
|
return backup
|
||||||
|
|
||||||
def load(self) -> Awaitable[None]:
|
def load(self) -> Awaitable[None]:
|
||||||
"""Load exists backups data.
|
"""Load existing backups data.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.reload()
|
return self.reload()
|
||||||
|
|
||||||
|
@Job(name="backup_reload")
|
||||||
async def reload(
|
async def reload(
|
||||||
self,
|
self,
|
||||||
location: LOCATION_TYPE | type[DEFAULT] = DEFAULT,
|
location: LOCATION_TYPE | type[DEFAULT] = DEFAULT,
|
||||||
filename: str | None = None,
|
filename: str | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Load exists backups."""
|
"""Load existing backups."""
|
||||||
|
|
||||||
async def _load_backup(location: str | None, tar_file: Path) -> bool:
|
async def _load_backup(location: str | None, tar_file: Path) -> bool:
|
||||||
"""Load the backup."""
|
"""Load the backup."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user