mirror of
https://github.com/home-assistant/core.git
synced 2025-08-11 16:45:19 +02:00
Rename backup slug to backup_id (#130902)
This commit is contained in:
@@ -8,7 +8,7 @@ from homeassistant.helpers import config_validation as cv
|
|||||||
from homeassistant.helpers.hassio import is_hassio
|
from homeassistant.helpers.hassio import is_hassio
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .agent import BackupAgent, BackupAgentPlatformProtocol, UploadedBackup
|
from .agent import BackupAgent, BackupAgentPlatformProtocol
|
||||||
from .const import DOMAIN, LOGGER
|
from .const import DOMAIN, LOGGER
|
||||||
from .http import async_register_http_views
|
from .http import async_register_http_views
|
||||||
from .manager import Backup, BackupManager, BackupPlatformProtocol
|
from .manager import Backup, BackupManager, BackupPlatformProtocol
|
||||||
@@ -22,7 +22,6 @@ __all__ = [
|
|||||||
"BackupPlatformProtocol",
|
"BackupPlatformProtocol",
|
||||||
"BackupUploadMetadata",
|
"BackupUploadMetadata",
|
||||||
"BaseBackup",
|
"BaseBackup",
|
||||||
"UploadedBackup",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Protocol
|
from typing import Any, Protocol
|
||||||
|
|
||||||
@@ -23,13 +22,6 @@ class BackupAgentUnreachableError(BackupAgentError):
|
|||||||
_message = "The backup agent is unreachable."
|
_message = "The backup agent is unreachable."
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
|
||||||
class UploadedBackup(BaseBackup):
|
|
||||||
"""Uploaded backup class."""
|
|
||||||
|
|
||||||
id: str
|
|
||||||
|
|
||||||
|
|
||||||
class BackupAgent(abc.ABC):
|
class BackupAgent(abc.ABC):
|
||||||
"""Backup agent interface."""
|
"""Backup agent interface."""
|
||||||
|
|
||||||
@@ -38,14 +30,14 @@ class BackupAgent(abc.ABC):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_download_backup(
|
async def async_download_backup(
|
||||||
self,
|
self,
|
||||||
|
backup_id: str,
|
||||||
*,
|
*,
|
||||||
id: str,
|
|
||||||
path: Path,
|
path: Path,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Download a backup file.
|
"""Download a backup file.
|
||||||
|
|
||||||
:param id: The ID of the backup that was returned in async_list_backups.
|
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
||||||
:param path: The full file path to download the backup to.
|
:param path: The full file path to download the backup to.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -64,16 +56,15 @@ class BackupAgent(abc.ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]:
|
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
|
||||||
"""List backups."""
|
"""List backups."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
backup_id: str,
|
||||||
slug: str,
|
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> UploadedBackup | None:
|
) -> BaseBackup | None:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
|
|
||||||
|
|
||||||
@@ -81,10 +72,10 @@ class LocalBackupAgent(BackupAgent):
|
|||||||
"""Local backup agent."""
|
"""Local backup agent."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_backup_path(self, slug: str) -> Path:
|
def get_backup_path(self, backup_id: str) -> Path:
|
||||||
"""Return the local path to a backup.
|
"""Return the local path to a backup.
|
||||||
|
|
||||||
The method should return the path to the backup file with the specified slug.
|
The method should return the path to the backup file with the specified id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,9 +10,9 @@ from typing import Any
|
|||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .agent import BackupAgent, LocalBackupAgent, UploadedBackup
|
from .agent import BackupAgent, LocalBackupAgent
|
||||||
from .const import LOGGER
|
from .const import LOGGER
|
||||||
from .models import BackupUploadMetadata
|
from .models import BackupUploadMetadata, BaseBackup
|
||||||
from .util import read_backup
|
from .util import read_backup
|
||||||
|
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ async def async_get_backup_agents(
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class LocalBackup(UploadedBackup):
|
class LocalBackup(BaseBackup):
|
||||||
"""Local backup class."""
|
"""Local backup class."""
|
||||||
|
|
||||||
path: Path
|
path: Path
|
||||||
@@ -62,23 +62,22 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
try:
|
try:
|
||||||
base_backup = read_backup(backup_path)
|
base_backup = read_backup(backup_path)
|
||||||
backup = LocalBackup(
|
backup = LocalBackup(
|
||||||
id=base_backup.slug,
|
backup_id=base_backup.backup_id,
|
||||||
slug=base_backup.slug,
|
|
||||||
name=base_backup.name,
|
name=base_backup.name,
|
||||||
date=base_backup.date,
|
date=base_backup.date,
|
||||||
path=backup_path,
|
path=backup_path,
|
||||||
size=round(backup_path.stat().st_size / 1_048_576, 2),
|
size=round(backup_path.stat().st_size / 1_048_576, 2),
|
||||||
protected=base_backup.protected,
|
protected=base_backup.protected,
|
||||||
)
|
)
|
||||||
backups[backup.slug] = backup
|
backups[backup.backup_id] = backup
|
||||||
except (OSError, TarError, json.JSONDecodeError, KeyError) as err:
|
except (OSError, TarError, json.JSONDecodeError, KeyError) as err:
|
||||||
LOGGER.warning("Unable to read backup %s: %s", backup_path, err)
|
LOGGER.warning("Unable to read backup %s: %s", backup_path, err)
|
||||||
return backups
|
return backups
|
||||||
|
|
||||||
async def async_download_backup(
|
async def async_download_backup(
|
||||||
self,
|
self,
|
||||||
|
backup_id: str,
|
||||||
*,
|
*,
|
||||||
id: str,
|
|
||||||
path: Path,
|
path: Path,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -93,17 +92,16 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Upload a backup."""
|
"""Upload a backup."""
|
||||||
self._backups[metadata.slug] = LocalBackup(
|
self._backups[metadata.backup_id] = LocalBackup(
|
||||||
id=metadata.slug, # Do we need another ID?
|
backup_id=metadata.backup_id,
|
||||||
slug=metadata.slug,
|
|
||||||
name=metadata.name,
|
|
||||||
date=metadata.date,
|
date=metadata.date,
|
||||||
|
name=metadata.name,
|
||||||
path=path,
|
path=path,
|
||||||
size=round(path.stat().st_size / 1_048_576, 2),
|
|
||||||
protected=metadata.protected,
|
protected=metadata.protected,
|
||||||
|
size=round(path.stat().st_size / 1_048_576, 2),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]:
|
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
|
||||||
"""List backups."""
|
"""List backups."""
|
||||||
if not self._loaded_backups:
|
if not self._loaded_backups:
|
||||||
await self.load_backups()
|
await self.load_backups()
|
||||||
@@ -111,15 +109,14 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
|
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
backup_id: str,
|
||||||
slug: str,
|
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> UploadedBackup | None:
|
) -> LocalBackup | None:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
if not self._loaded_backups:
|
if not self._loaded_backups:
|
||||||
await self.load_backups()
|
await self.load_backups()
|
||||||
|
|
||||||
if not (backup := self._backups.get(slug)):
|
if not (backup := self._backups.get(backup_id)):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not await self._hass.async_add_executor_job(backup.path.exists):
|
if not await self._hass.async_add_executor_job(backup.path.exists):
|
||||||
@@ -128,23 +125,23 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
"Removing tracked backup (%s) that does not exists on the expected"
|
"Removing tracked backup (%s) that does not exists on the expected"
|
||||||
" path %s"
|
" path %s"
|
||||||
),
|
),
|
||||||
backup.slug,
|
backup.backup_id,
|
||||||
backup.path,
|
backup.path,
|
||||||
)
|
)
|
||||||
self._backups.pop(slug)
|
self._backups.pop(backup_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return backup
|
return backup
|
||||||
|
|
||||||
def get_backup_path(self, slug: str) -> Path:
|
def get_backup_path(self, backup_id: str) -> Path:
|
||||||
"""Return the local path to a backup."""
|
"""Return the local path to a backup."""
|
||||||
return self._backup_dir / f"{slug}.tar"
|
return self._backup_dir / f"{backup_id}.tar"
|
||||||
|
|
||||||
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None:
|
async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
|
||||||
"""Remove a backup."""
|
"""Remove a backup."""
|
||||||
if (backup := await self.async_get_backup(slug=slug)) is None:
|
if (backup := await self.async_get_backup(backup_id)) is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
await self._hass.async_add_executor_job(backup.path.unlink, True) # type: ignore[attr-defined]
|
await self._hass.async_add_executor_job(backup.path.unlink, True)
|
||||||
LOGGER.debug("Removed backup located at %s", backup.path) # type: ignore[attr-defined]
|
LOGGER.debug("Removed backup located at %s", backup.path)
|
||||||
self._backups.pop(slug)
|
self._backups.pop(backup_id)
|
||||||
|
@@ -31,13 +31,13 @@ def async_register_http_views(hass: HomeAssistant) -> None:
|
|||||||
class DownloadBackupView(HomeAssistantView):
|
class DownloadBackupView(HomeAssistantView):
|
||||||
"""Generate backup view."""
|
"""Generate backup view."""
|
||||||
|
|
||||||
url = "/api/backup/download/{slug}"
|
url = "/api/backup/download/{backup_id}"
|
||||||
name = "api:backup:download"
|
name = "api:backup:download"
|
||||||
|
|
||||||
async def get(
|
async def get(
|
||||||
self,
|
self,
|
||||||
request: Request,
|
request: Request,
|
||||||
slug: str,
|
backup_id: str,
|
||||||
) -> FileResponse | Response:
|
) -> FileResponse | Response:
|
||||||
"""Download a backup file."""
|
"""Download a backup file."""
|
||||||
if not request["hass_user"].is_admin:
|
if not request["hass_user"].is_admin:
|
||||||
@@ -51,7 +51,7 @@ class DownloadBackupView(HomeAssistantView):
|
|||||||
if agent_id not in manager.backup_agents:
|
if agent_id not in manager.backup_agents:
|
||||||
return Response(status=HTTPStatus.BAD_REQUEST)
|
return Response(status=HTTPStatus.BAD_REQUEST)
|
||||||
agent = manager.backup_agents[agent_id]
|
agent = manager.backup_agents[agent_id]
|
||||||
backup = await agent.async_get_backup(slug=slug)
|
backup = await agent.async_get_backup(backup_id)
|
||||||
|
|
||||||
# We don't need to check if the path exists, aiohttp.FileResponse will handle
|
# We don't need to check if the path exists, aiohttp.FileResponse will handle
|
||||||
# that
|
# that
|
||||||
@@ -60,10 +60,10 @@ class DownloadBackupView(HomeAssistantView):
|
|||||||
|
|
||||||
if agent_id in manager.local_backup_agents:
|
if agent_id in manager.local_backup_agents:
|
||||||
local_agent = manager.local_backup_agents[agent_id]
|
local_agent = manager.local_backup_agents[agent_id]
|
||||||
path = local_agent.get_backup_path(slug=slug)
|
path = local_agent.get_backup_path(backup_id)
|
||||||
else:
|
else:
|
||||||
path = manager.temp_backup_dir / f"{slug}.tar"
|
path = manager.temp_backup_dir / f"{backup_id}.tar"
|
||||||
await agent.async_download_backup(id=backup.id, path=path)
|
await agent.async_download_backup(backup_id, path=path)
|
||||||
|
|
||||||
# TODO: We need a callback to remove the temp file once the download is complete
|
# TODO: We need a callback to remove the temp file once the download is complete
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
|
@@ -53,7 +53,7 @@ _BackupT = TypeVar("_BackupT", bound=BaseBackup, default=BaseBackup)
|
|||||||
class NewBackup:
|
class NewBackup:
|
||||||
"""New backup class."""
|
"""New backup class."""
|
||||||
|
|
||||||
slug: str
|
backup_id: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
@@ -185,7 +185,7 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_restore_backup(
|
async def async_restore_backup(
|
||||||
self,
|
self,
|
||||||
slug: str,
|
backup_id: str,
|
||||||
*,
|
*,
|
||||||
agent_id: str,
|
agent_id: str,
|
||||||
password: str | None,
|
password: str | None,
|
||||||
@@ -218,17 +218,17 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
|
|||||||
) -> tuple[dict[str, Backup], dict[str, Exception]]:
|
) -> tuple[dict[str, Backup], dict[str, Exception]]:
|
||||||
"""Get backups.
|
"""Get backups.
|
||||||
|
|
||||||
Return a dictionary of Backup instances keyed by their slug.
|
Return a dictionary of Backup instances keyed by their ID.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self, *, slug: str, **kwargs: Any
|
self, backup_id: str, **kwargs: Any
|
||||||
) -> tuple[_BackupT | None, dict[str, Exception]]:
|
) -> tuple[_BackupT | None, dict[str, Exception]]:
|
||||||
"""Get a backup."""
|
"""Get a backup."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None:
|
async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
|
||||||
"""Remove a backup."""
|
"""Remove a backup."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@@ -265,12 +265,12 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
self.backup_agents[agent_id].async_upload_backup(
|
self.backup_agents[agent_id].async_upload_backup(
|
||||||
path=path,
|
path=path,
|
||||||
metadata=BackupUploadMetadata(
|
metadata=BackupUploadMetadata(
|
||||||
homeassistant=HAVERSION,
|
backup_id=backup.backup_id,
|
||||||
size=backup.size,
|
|
||||||
date=backup.date,
|
date=backup.date,
|
||||||
slug=backup.slug,
|
homeassistant=HAVERSION,
|
||||||
name=backup.name,
|
name=backup.name,
|
||||||
protected=backup.protected,
|
protected=backup.protected,
|
||||||
|
size=backup.size,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for agent_id in agent_ids
|
for agent_id in agent_ids
|
||||||
@@ -304,21 +304,21 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
if isinstance(result, BaseException):
|
if isinstance(result, BaseException):
|
||||||
raise result
|
raise result
|
||||||
for agent_backup in result:
|
for agent_backup in result:
|
||||||
if agent_backup.slug not in backups:
|
if agent_backup.backup_id not in backups:
|
||||||
backups[agent_backup.slug] = Backup(
|
backups[agent_backup.backup_id] = Backup(
|
||||||
slug=agent_backup.slug,
|
|
||||||
name=agent_backup.name,
|
|
||||||
date=agent_backup.date,
|
|
||||||
agent_ids=[],
|
agent_ids=[],
|
||||||
size=agent_backup.size,
|
backup_id=agent_backup.backup_id,
|
||||||
|
date=agent_backup.date,
|
||||||
|
name=agent_backup.name,
|
||||||
protected=agent_backup.protected,
|
protected=agent_backup.protected,
|
||||||
|
size=agent_backup.size,
|
||||||
)
|
)
|
||||||
backups[agent_backup.slug].agent_ids.append(agent_ids[idx])
|
backups[agent_backup.backup_id].agent_ids.append(agent_ids[idx])
|
||||||
|
|
||||||
return (backups, agent_errors)
|
return (backups, agent_errors)
|
||||||
|
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self, *, slug: str, **kwargs: Any
|
self, backup_id: str, **kwargs: Any
|
||||||
) -> tuple[Backup | None, dict[str, Exception]]:
|
) -> tuple[Backup | None, dict[str, Exception]]:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
backup: Backup | None = None
|
backup: Backup | None = None
|
||||||
@@ -327,7 +327,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
|
|
||||||
get_backup_results = await asyncio.gather(
|
get_backup_results = await asyncio.gather(
|
||||||
*(
|
*(
|
||||||
agent.async_get_backup(slug=slug)
|
agent.async_get_backup(backup_id)
|
||||||
for agent in self.backup_agents.values()
|
for agent in self.backup_agents.values()
|
||||||
),
|
),
|
||||||
return_exceptions=True,
|
return_exceptions=True,
|
||||||
@@ -342,23 +342,23 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
continue
|
continue
|
||||||
if backup is None:
|
if backup is None:
|
||||||
backup = Backup(
|
backup = Backup(
|
||||||
slug=result.slug,
|
|
||||||
name=result.name,
|
|
||||||
date=result.date,
|
|
||||||
agent_ids=[],
|
agent_ids=[],
|
||||||
size=result.size,
|
backup_id=result.backup_id,
|
||||||
|
date=result.date,
|
||||||
|
name=result.name,
|
||||||
protected=result.protected,
|
protected=result.protected,
|
||||||
|
size=result.size,
|
||||||
)
|
)
|
||||||
backup.agent_ids.append(agent_ids[idx])
|
backup.agent_ids.append(agent_ids[idx])
|
||||||
|
|
||||||
return (backup, agent_errors)
|
return (backup, agent_errors)
|
||||||
|
|
||||||
async def async_remove_backup(self, *, slug: str, **kwargs: Any) -> None:
|
async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
|
||||||
"""Remove a backup."""
|
"""Remove a backup."""
|
||||||
for agent in self.backup_agents.values():
|
for agent in self.backup_agents.values():
|
||||||
if not hasattr(agent, "async_remove_backup"):
|
if not hasattr(agent, "async_remove_backup"):
|
||||||
continue
|
continue
|
||||||
await agent.async_remove_backup(slug=slug)
|
await agent.async_remove_backup(backup_id)
|
||||||
|
|
||||||
async def async_receive_backup(
|
async def async_receive_backup(
|
||||||
self,
|
self,
|
||||||
@@ -415,7 +415,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
if local_file_paths:
|
if local_file_paths:
|
||||||
tar_file_path = local_file_paths[0]
|
tar_file_path = local_file_paths[0]
|
||||||
else:
|
else:
|
||||||
tar_file_path = self.temp_backup_dir / f"{backup.slug}.tar"
|
tar_file_path = self.temp_backup_dir / f"{backup.backup_id}.tar"
|
||||||
for local_path in local_file_paths:
|
for local_path in local_file_paths:
|
||||||
shutil.copy(target_temp_file, local_path)
|
shutil.copy(target_temp_file, local_path)
|
||||||
temp_dir_handler.cleanup()
|
temp_dir_handler.cleanup()
|
||||||
@@ -430,7 +430,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
return
|
return
|
||||||
|
|
||||||
local_file_paths = [
|
local_file_paths = [
|
||||||
self.local_backup_agents[agent_id].get_backup_path(backup.slug)
|
self.local_backup_agents[agent_id].get_backup_path(backup.backup_id)
|
||||||
for agent_id in agent_ids
|
for agent_id in agent_ids
|
||||||
if agent_id in self.local_backup_agents
|
if agent_id in self.local_backup_agents
|
||||||
]
|
]
|
||||||
@@ -464,23 +464,23 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
raise HomeAssistantError("Invalid agent selected")
|
raise HomeAssistantError("Invalid agent selected")
|
||||||
backup_name = name or f"Core {HAVERSION}"
|
backup_name = name or f"Core {HAVERSION}"
|
||||||
date_str = dt_util.now().isoformat()
|
date_str = dt_util.now().isoformat()
|
||||||
slug = _generate_slug(date_str, backup_name)
|
backup_id = _generate_backup_id(date_str, backup_name)
|
||||||
self.backup_task = self.hass.async_create_task(
|
self.backup_task = self.hass.async_create_task(
|
||||||
self._async_create_backup(
|
self._async_create_backup(
|
||||||
addons_included=addons_included,
|
addons_included=addons_included,
|
||||||
agent_ids=agent_ids,
|
agent_ids=agent_ids,
|
||||||
|
backup_id=backup_id,
|
||||||
backup_name=backup_name,
|
backup_name=backup_name,
|
||||||
database_included=database_included,
|
database_included=database_included,
|
||||||
date_str=date_str,
|
date_str=date_str,
|
||||||
folders_included=folders_included,
|
folders_included=folders_included,
|
||||||
on_progress=on_progress,
|
on_progress=on_progress,
|
||||||
password=password,
|
password=password,
|
||||||
slug=slug,
|
|
||||||
),
|
),
|
||||||
name="backup_manager_create_backup",
|
name="backup_manager_create_backup",
|
||||||
eager_start=False, # To ensure the task is not started before we return
|
eager_start=False, # To ensure the task is not started before we return
|
||||||
)
|
)
|
||||||
return NewBackup(slug=slug)
|
return NewBackup(backup_id=backup_id)
|
||||||
|
|
||||||
async def _async_create_backup(
|
async def _async_create_backup(
|
||||||
self,
|
self,
|
||||||
@@ -493,13 +493,13 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
folders_included: list[str] | None,
|
folders_included: list[str] | None,
|
||||||
on_progress: Callable[[BackupProgress], None] | None,
|
on_progress: Callable[[BackupProgress], None] | None,
|
||||||
password: str | None,
|
password: str | None,
|
||||||
slug: str,
|
backup_id: str,
|
||||||
) -> BaseBackup:
|
) -> BaseBackup:
|
||||||
"""Generate a backup."""
|
"""Generate a backup."""
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
local_file_paths = [
|
local_file_paths = [
|
||||||
self.local_backup_agents[agent_id].get_backup_path(slug)
|
self.local_backup_agents[agent_id].get_backup_path(backup_id)
|
||||||
for agent_id in agent_ids
|
for agent_id in agent_ids
|
||||||
if agent_id in self.local_backup_agents
|
if agent_id in self.local_backup_agents
|
||||||
]
|
]
|
||||||
@@ -508,17 +508,17 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
await self.async_pre_backup_actions()
|
await self.async_pre_backup_actions()
|
||||||
|
|
||||||
backup_data = {
|
backup_data = {
|
||||||
"slug": slug,
|
"compressed": True,
|
||||||
"name": backup_name,
|
|
||||||
"date": date_str,
|
"date": date_str,
|
||||||
"type": "partial",
|
|
||||||
"folders": ["homeassistant"],
|
"folders": ["homeassistant"],
|
||||||
"homeassistant": {
|
"homeassistant": {
|
||||||
"exclude_database": not database_included,
|
"exclude_database": not database_included,
|
||||||
"version": HAVERSION,
|
"version": HAVERSION,
|
||||||
},
|
},
|
||||||
"compressed": True,
|
"name": backup_name,
|
||||||
"protected": password is not None,
|
"protected": password is not None,
|
||||||
|
"slug": backup_id,
|
||||||
|
"type": "partial",
|
||||||
}
|
}
|
||||||
|
|
||||||
tar_file_path, size_in_bytes = await self.hass.async_add_executor_job(
|
tar_file_path, size_in_bytes = await self.hass.async_add_executor_job(
|
||||||
@@ -529,15 +529,15 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
password,
|
password,
|
||||||
)
|
)
|
||||||
backup = BaseBackup(
|
backup = BaseBackup(
|
||||||
slug=slug,
|
backup_id=backup_id,
|
||||||
name=backup_name,
|
|
||||||
date=date_str,
|
date=date_str,
|
||||||
size=round(size_in_bytes / 1_048_576, 2),
|
name=backup_name,
|
||||||
protected=password is not None,
|
protected=password is not None,
|
||||||
|
size=round(size_in_bytes / 1_048_576, 2),
|
||||||
)
|
)
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Generated new backup with slug %s, uploading to agents %s",
|
"Generated new backup with backup_id %s, uploading to agents %s",
|
||||||
slug,
|
backup_id,
|
||||||
agent_ids,
|
agent_ids,
|
||||||
)
|
)
|
||||||
await self._async_upload_backup(
|
await self._async_upload_backup(
|
||||||
@@ -564,7 +564,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
if tar_file_paths:
|
if tar_file_paths:
|
||||||
tar_file_path = tar_file_paths[0]
|
tar_file_path = tar_file_paths[0]
|
||||||
else:
|
else:
|
||||||
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar"
|
tar_file_path = self.temp_backup_dir / f"{backup_data['backup_id']}.tar"
|
||||||
if not (backup_dir := tar_file_path.parent).exists():
|
if not (backup_dir := tar_file_path.parent).exists():
|
||||||
LOGGER.debug("Creating backup directory %s", backup_dir)
|
LOGGER.debug("Creating backup directory %s", backup_dir)
|
||||||
backup_dir.mkdir()
|
backup_dir.mkdir()
|
||||||
@@ -600,7 +600,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
|
|
||||||
async def async_restore_backup(
|
async def async_restore_backup(
|
||||||
self,
|
self,
|
||||||
slug: str,
|
backup_id: str,
|
||||||
*,
|
*,
|
||||||
agent_id: str,
|
agent_id: str,
|
||||||
password: str | None,
|
password: str | None,
|
||||||
@@ -614,17 +614,21 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
|
|
||||||
if agent_id in self.local_backup_agents:
|
if agent_id in self.local_backup_agents:
|
||||||
local_agent = self.local_backup_agents[agent_id]
|
local_agent = self.local_backup_agents[agent_id]
|
||||||
if not await local_agent.async_get_backup(slug=slug):
|
if not await local_agent.async_get_backup(backup_id):
|
||||||
raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}")
|
raise HomeAssistantError(
|
||||||
path = local_agent.get_backup_path(slug=slug)
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
|
)
|
||||||
|
path = local_agent.get_backup_path(backup_id)
|
||||||
else:
|
else:
|
||||||
path = self.temp_backup_dir / f"{slug}.tar"
|
path = self.temp_backup_dir / f"{backup_id}.tar"
|
||||||
agent = self.backup_agents[agent_id]
|
agent = self.backup_agents[agent_id]
|
||||||
if not (backup := await agent.async_get_backup(slug=slug)):
|
if not await agent.async_get_backup(backup_id):
|
||||||
raise HomeAssistantError(f"Backup {slug} not found in agent {agent_id}")
|
raise HomeAssistantError(
|
||||||
await agent.async_download_backup(id=backup.id, path=path)
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
|
)
|
||||||
|
await agent.async_download_backup(backup_id, path=path)
|
||||||
|
|
||||||
path = local_agent.get_backup_path(slug)
|
path = local_agent.get_backup_path(backup_id)
|
||||||
|
|
||||||
def _write_restore_file() -> None:
|
def _write_restore_file() -> None:
|
||||||
"""Write the restore file."""
|
"""Write the restore file."""
|
||||||
@@ -637,6 +641,6 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
await self.hass.services.async_call("homeassistant", "restart", {})
|
await self.hass.services.async_call("homeassistant", "restart", {})
|
||||||
|
|
||||||
|
|
||||||
def _generate_slug(date: str, name: str) -> str:
|
def _generate_backup_id(date: str, name: str) -> str:
|
||||||
"""Generate a backup slug."""
|
"""Generate a backup ID."""
|
||||||
return hashlib.sha1(f"{date} - {name}".lower().encode()).hexdigest()[:8]
|
return hashlib.sha1(f"{date} - {name}".lower().encode()).hexdigest()[:8]
|
||||||
|
@@ -7,10 +7,10 @@ from dataclasses import asdict, dataclass
|
|||||||
class BaseBackup:
|
class BaseBackup:
|
||||||
"""Base backup class."""
|
"""Base backup class."""
|
||||||
|
|
||||||
|
backup_id: str
|
||||||
date: str
|
date: str
|
||||||
name: str
|
name: str
|
||||||
protected: bool
|
protected: bool
|
||||||
slug: str
|
|
||||||
size: float
|
size: float
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
@@ -22,9 +22,9 @@ class BaseBackup:
|
|||||||
class BackupUploadMetadata:
|
class BackupUploadMetadata:
|
||||||
"""Backup upload metadata."""
|
"""Backup upload metadata."""
|
||||||
|
|
||||||
|
backup_id: str # The ID of the backup
|
||||||
date: str # The date the backup was created
|
date: str # The date the backup was created
|
||||||
slug: str # The slug of the backup
|
|
||||||
size: float # The size of the backup (in bytes)
|
|
||||||
name: str # The name of the backup
|
|
||||||
homeassistant: str # The version of Home Assistant that created the backup
|
homeassistant: str # The version of Home Assistant that created the backup
|
||||||
|
name: str # The name of the backup
|
||||||
protected: bool # If the backup is protected
|
protected: bool # If the backup is protected
|
||||||
|
size: float # The size of the backup (in bytes)
|
||||||
|
@@ -20,9 +20,9 @@ def read_backup(backup_path: Path) -> BaseBackup:
|
|||||||
raise KeyError("backup.json not found in tar file")
|
raise KeyError("backup.json not found in tar file")
|
||||||
data = json_loads_object(data_file.read())
|
data = json_loads_object(data_file.read())
|
||||||
return BaseBackup(
|
return BaseBackup(
|
||||||
slug=cast(str, data["slug"]),
|
backup_id=cast(str, data["slug"]),
|
||||||
name=cast(str, data["name"]),
|
|
||||||
date=cast(str, data["date"]),
|
date=cast(str, data["date"]),
|
||||||
size=round(backup_path.stat().st_size / 1_048_576, 2),
|
name=cast(str, data["name"]),
|
||||||
protected=cast(bool, data.get("protected", False)),
|
protected=cast(bool, data.get("protected", False)),
|
||||||
|
size=round(backup_path.stat().st_size / 1_048_576, 2),
|
||||||
)
|
)
|
||||||
|
@@ -60,7 +60,7 @@ async def handle_info(
|
|||||||
@websocket_api.websocket_command(
|
@websocket_api.websocket_command(
|
||||||
{
|
{
|
||||||
vol.Required("type"): "backup/details",
|
vol.Required("type"): "backup/details",
|
||||||
vol.Required("slug"): str,
|
vol.Required("backup_id"): str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@websocket_api.async_response
|
@websocket_api.async_response
|
||||||
@@ -69,9 +69,9 @@ async def handle_details(
|
|||||||
connection: websocket_api.ActiveConnection,
|
connection: websocket_api.ActiveConnection,
|
||||||
msg: dict[str, Any],
|
msg: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Get backup details for a specific slug."""
|
"""Get backup details for a specific backup."""
|
||||||
backup, agent_errors = await hass.data[DATA_MANAGER].async_get_backup(
|
backup, agent_errors = await hass.data[DATA_MANAGER].async_get_backup(
|
||||||
slug=msg["slug"]
|
msg["backup_id"]
|
||||||
)
|
)
|
||||||
connection.send_result(
|
connection.send_result(
|
||||||
msg["id"],
|
msg["id"],
|
||||||
@@ -88,7 +88,7 @@ async def handle_details(
|
|||||||
@websocket_api.websocket_command(
|
@websocket_api.websocket_command(
|
||||||
{
|
{
|
||||||
vol.Required("type"): "backup/remove",
|
vol.Required("type"): "backup/remove",
|
||||||
vol.Required("slug"): str,
|
vol.Required("backup_id"): str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@websocket_api.async_response
|
@websocket_api.async_response
|
||||||
@@ -98,7 +98,7 @@ async def handle_remove(
|
|||||||
msg: dict[str, Any],
|
msg: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Remove a backup."""
|
"""Remove a backup."""
|
||||||
await hass.data[DATA_MANAGER].async_remove_backup(slug=msg["slug"])
|
await hass.data[DATA_MANAGER].async_remove_backup(msg["backup_id"])
|
||||||
connection.send_result(msg["id"])
|
connection.send_result(msg["id"])
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ async def handle_remove(
|
|||||||
@websocket_api.websocket_command(
|
@websocket_api.websocket_command(
|
||||||
{
|
{
|
||||||
vol.Required("type"): "backup/restore",
|
vol.Required("type"): "backup/restore",
|
||||||
vol.Required("slug"): str,
|
vol.Required("backup_id"): str,
|
||||||
vol.Required("agent_id"): str,
|
vol.Required("agent_id"): str,
|
||||||
vol.Optional("password"): str,
|
vol.Optional("password"): str,
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ async def handle_restore(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Restore a backup."""
|
"""Restore a backup."""
|
||||||
await hass.data[DATA_MANAGER].async_restore_backup(
|
await hass.data[DATA_MANAGER].async_restore_backup(
|
||||||
slug=msg["slug"],
|
msg["backup_id"],
|
||||||
agent_id=msg["agent_id"],
|
agent_id=msg["agent_id"],
|
||||||
password=msg.get("password"),
|
password=msg.get("password"),
|
||||||
)
|
)
|
||||||
@@ -245,7 +245,6 @@ async def backup_agents_list_backups(
|
|||||||
vol.Required("type"): "backup/agents/download",
|
vol.Required("type"): "backup/agents/download",
|
||||||
vol.Required("agent_id"): str,
|
vol.Required("agent_id"): str,
|
||||||
vol.Required("backup_id"): str,
|
vol.Required("backup_id"): str,
|
||||||
vol.Required("slug"): str,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@websocket_api.async_response
|
@websocket_api.async_response
|
||||||
@@ -262,9 +261,9 @@ async def backup_agents_download(
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
path = manager.temp_backup_dir / f"{msg["slug"]}.tar"
|
path = manager.temp_backup_dir / f"{msg["backup_id"]}.tar"
|
||||||
await agent.async_download_backup(
|
await agent.async_download_backup(
|
||||||
id=msg["backup_id"],
|
msg["backup_id"],
|
||||||
path=path,
|
path=path,
|
||||||
)
|
)
|
||||||
except Exception as err: # noqa: BLE001
|
except Exception as err: # noqa: BLE001
|
||||||
|
@@ -5,12 +5,11 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from homeassistant.components.backup import (
|
from homeassistant.components.backup import (
|
||||||
BackupAgent,
|
BackupAgent,
|
||||||
BackupUploadMetadata,
|
BackupUploadMetadata,
|
||||||
UploadedBackup,
|
BaseBackup,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
@@ -32,25 +31,23 @@ class KitchenSinkBackupAgent(BackupAgent):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self._uploads = [
|
self._uploads = [
|
||||||
UploadedBackup(
|
BaseBackup(
|
||||||
id="def456",
|
backup_id="abc123",
|
||||||
|
date="1970-01-01T00:00:00Z",
|
||||||
name="Kitchen sink syncer",
|
name="Kitchen sink syncer",
|
||||||
protected=False,
|
protected=False,
|
||||||
slug="abc123",
|
|
||||||
size=1234,
|
size=1234,
|
||||||
date="1970-01-01T00:00:00Z",
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
async def async_download_backup(
|
async def async_download_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
backup_id: str,
|
||||||
id: str,
|
|
||||||
path: Path,
|
path: Path,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Download a backup file."""
|
"""Download a backup file."""
|
||||||
LOGGER.info("Downloading backup %s to %s", id, path)
|
LOGGER.info("Downloading backup %s to %s", backup_id, path)
|
||||||
|
|
||||||
async def async_upload_backup(
|
async def async_upload_backup(
|
||||||
self,
|
self,
|
||||||
@@ -62,28 +59,26 @@ class KitchenSinkBackupAgent(BackupAgent):
|
|||||||
"""Upload a backup."""
|
"""Upload a backup."""
|
||||||
LOGGER.info("Uploading backup %s %s", path.name, metadata)
|
LOGGER.info("Uploading backup %s %s", path.name, metadata)
|
||||||
self._uploads.append(
|
self._uploads.append(
|
||||||
UploadedBackup(
|
BaseBackup(
|
||||||
id=uuid4().hex,
|
backup_id=metadata.backup_id,
|
||||||
|
date=metadata.date,
|
||||||
name=metadata.name,
|
name=metadata.name,
|
||||||
protected=metadata.protected,
|
protected=metadata.protected,
|
||||||
slug=metadata.slug,
|
|
||||||
size=metadata.size,
|
size=metadata.size,
|
||||||
date=metadata.date,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]:
|
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
|
||||||
"""List synced backups."""
|
"""List synced backups."""
|
||||||
return self._uploads
|
return self._uploads
|
||||||
|
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
backup_id: str,
|
||||||
slug: str,
|
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> UploadedBackup | None:
|
) -> BaseBackup | None:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
for backup in self._uploads:
|
for backup in self._uploads:
|
||||||
if backup.slug == slug:
|
if backup.backup_id == backup_id:
|
||||||
return backup
|
return backup
|
||||||
return None
|
return None
|
||||||
|
@@ -10,12 +10,11 @@ from homeassistant.components.backup import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
BackupAgent,
|
BackupAgent,
|
||||||
BackupUploadMetadata,
|
BackupUploadMetadata,
|
||||||
UploadedBackup,
|
BaseBackup,
|
||||||
)
|
)
|
||||||
from homeassistant.components.backup.backup import LocalBackup
|
from homeassistant.components.backup.backup import LocalBackup
|
||||||
from homeassistant.components.backup.const import DATA_MANAGER
|
from homeassistant.components.backup.const import DATA_MANAGER
|
||||||
from homeassistant.components.backup.manager import Backup
|
from homeassistant.components.backup.manager import Backup
|
||||||
from homeassistant.components.backup.models import BaseBackup
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@@ -23,29 +22,28 @@ from homeassistant.setup import async_setup_component
|
|||||||
LOCAL_AGENT_ID = f"{DOMAIN}.local"
|
LOCAL_AGENT_ID = f"{DOMAIN}.local"
|
||||||
|
|
||||||
TEST_BASE_BACKUP = BaseBackup(
|
TEST_BASE_BACKUP = BaseBackup(
|
||||||
slug="abc123",
|
backup_id="abc123",
|
||||||
name="Test",
|
|
||||||
date="1970-01-01T00:00:00.000Z",
|
date="1970-01-01T00:00:00.000Z",
|
||||||
size=0.0,
|
name="Test",
|
||||||
protected=False,
|
protected=False,
|
||||||
|
size=0.0,
|
||||||
)
|
)
|
||||||
TEST_BACKUP = Backup(
|
TEST_BACKUP = Backup(
|
||||||
agent_ids=["backup.local"],
|
agent_ids=["backup.local"],
|
||||||
slug="abc123",
|
backup_id="abc123",
|
||||||
name="Test",
|
|
||||||
date="1970-01-01T00:00:00.000Z",
|
date="1970-01-01T00:00:00.000Z",
|
||||||
size=0.0,
|
name="Test",
|
||||||
protected=False,
|
protected=False,
|
||||||
|
size=0.0,
|
||||||
)
|
)
|
||||||
TEST_BACKUP_PATH = Path("abc123.tar")
|
TEST_BACKUP_PATH = Path("abc123.tar")
|
||||||
TEST_LOCAL_BACKUP = LocalBackup(
|
TEST_LOCAL_BACKUP = LocalBackup(
|
||||||
id="abc123",
|
|
||||||
slug="abc123",
|
|
||||||
name="Test",
|
|
||||||
date="1970-01-01T00:00:00.000Z",
|
date="1970-01-01T00:00:00.000Z",
|
||||||
|
backup_id="abc123",
|
||||||
|
name="Test",
|
||||||
path=Path("abc123.tar"),
|
path=Path("abc123.tar"),
|
||||||
size=0.0,
|
|
||||||
protected=False,
|
protected=False,
|
||||||
|
size=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -74,35 +72,33 @@ class BackupAgentTest(BackupAgent):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Upload a backup."""
|
"""Upload a backup."""
|
||||||
|
|
||||||
async def async_list_backups(self, **kwargs: Any) -> list[UploadedBackup]:
|
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
|
||||||
"""List backups."""
|
"""List backups."""
|
||||||
return [
|
return [
|
||||||
UploadedBackup(
|
BaseBackup(
|
||||||
id="abc123",
|
backup_id="abc123",
|
||||||
date="1970-01-01T00:00:00Z",
|
date="1970-01-01T00:00:00Z",
|
||||||
name="Test",
|
name="Test",
|
||||||
protected=False,
|
protected=False,
|
||||||
size=13.37,
|
size=13.37,
|
||||||
slug="abc123",
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
async def async_get_backup(
|
async def async_get_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
slug: str,
|
backup_id: str,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> UploadedBackup | None:
|
) -> BaseBackup | None:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
if slug != "abc123":
|
if backup_id != "abc123":
|
||||||
return None
|
return None
|
||||||
return UploadedBackup(
|
return BaseBackup(
|
||||||
id="abc123",
|
backup_id="abc123",
|
||||||
date="1970-01-01T00:00:00Z",
|
date="1970-01-01T00:00:00Z",
|
||||||
name="Test",
|
name="Test",
|
||||||
protected=False,
|
protected=False,
|
||||||
size=13.37,
|
size=13.37,
|
||||||
slug="abc123",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -119,7 +115,7 @@ async def setup_backup_integration(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
local_agent = hass.data[DATA_MANAGER].backup_agents[LOCAL_AGENT_ID]
|
local_agent = hass.data[DATA_MANAGER].backup_agents[LOCAL_AGENT_ID]
|
||||||
local_agent._backups = {backups.slug: backups for backups in backups}
|
local_agent._backups = {backup.backup_id: backup for backup in backups}
|
||||||
local_agent._loaded_backups = True
|
local_agent._loaded_backups = True
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@@ -79,12 +79,11 @@
|
|||||||
'result': list([
|
'result': list([
|
||||||
dict({
|
dict({
|
||||||
'agent_id': 'domain.test',
|
'agent_id': 'domain.test',
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00Z',
|
'date': '1970-01-01T00:00:00Z',
|
||||||
'id': 'abc123',
|
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 13.37,
|
'size': 13.37,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
'success': True,
|
'success': True,
|
||||||
@@ -97,12 +96,11 @@
|
|||||||
'result': list([
|
'result': list([
|
||||||
dict({
|
dict({
|
||||||
'agent_id': 'domain.test',
|
'agent_id': 'domain.test',
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00Z',
|
'date': '1970-01-01T00:00:00Z',
|
||||||
'id': 'abc123',
|
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 13.37,
|
'size': 13.37,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
'success': True,
|
'success': True,
|
||||||
@@ -367,11 +365,11 @@
|
|||||||
'agent_ids': list([
|
'agent_ids': list([
|
||||||
'backup.local',
|
'backup.local',
|
||||||
]),
|
]),
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00.000Z',
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 0.0,
|
'size': 0.0,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
@@ -401,11 +399,11 @@
|
|||||||
'agent_ids': list([
|
'agent_ids': list([
|
||||||
'backup.local',
|
'backup.local',
|
||||||
]),
|
]),
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00.000Z',
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 0.0,
|
'size': 0.0,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
@@ -460,7 +458,7 @@
|
|||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': dict({
|
'result': dict({
|
||||||
'slug': '27f5c632',
|
'backup_id': '27f5c632',
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@@ -481,7 +479,7 @@
|
|||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': dict({
|
'result': dict({
|
||||||
'slug': '27f5c632',
|
'backup_id': '27f5c632',
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@@ -502,7 +500,7 @@
|
|||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': dict({
|
'result': dict({
|
||||||
'slug': '27f5c632',
|
'backup_id': '27f5c632',
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@@ -523,7 +521,7 @@
|
|||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': dict({
|
'result': dict({
|
||||||
'slug': 'abc123',
|
'backup_id': 'abc123',
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@@ -533,7 +531,7 @@
|
|||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': dict({
|
'result': dict({
|
||||||
'slug': 'abc123',
|
'backup_id': 'abc123',
|
||||||
}),
|
}),
|
||||||
'success': True,
|
'success': True,
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@@ -562,11 +560,11 @@
|
|||||||
'agent_ids': list([
|
'agent_ids': list([
|
||||||
'backup.local',
|
'backup.local',
|
||||||
]),
|
]),
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00.000Z',
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 0.0,
|
'size': 0.0,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
@@ -587,11 +585,11 @@
|
|||||||
'agent_ids': list([
|
'agent_ids': list([
|
||||||
'backup.local',
|
'backup.local',
|
||||||
]),
|
]),
|
||||||
|
'backup_id': 'abc123',
|
||||||
'date': '1970-01-01T00:00:00.000Z',
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'protected': False,
|
'protected': False,
|
||||||
'size': 0.0,
|
'size': 0.0,
|
||||||
'slug': 'abc123',
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
|
@@ -133,9 +133,9 @@ async def test_load_backups(hass: HomeAssistant) -> None:
|
|||||||
patch(
|
patch(
|
||||||
"homeassistant.components.backup.util.json_loads_object",
|
"homeassistant.components.backup.util.json_loads_object",
|
||||||
return_value={
|
return_value={
|
||||||
"slug": TEST_LOCAL_BACKUP.slug,
|
|
||||||
"name": TEST_LOCAL_BACKUP.name,
|
|
||||||
"date": TEST_LOCAL_BACKUP.date,
|
"date": TEST_LOCAL_BACKUP.date,
|
||||||
|
"name": TEST_LOCAL_BACKUP.name,
|
||||||
|
"slug": TEST_LOCAL_BACKUP.backup_id,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
@@ -145,7 +145,7 @@ async def test_load_backups(hass: HomeAssistant) -> None:
|
|||||||
):
|
):
|
||||||
await manager.backup_agents[LOCAL_AGENT_ID].load_backups()
|
await manager.backup_agents[LOCAL_AGENT_ID].load_backups()
|
||||||
backups, agent_errors = await manager.async_get_backups()
|
backups, agent_errors = await manager.async_get_backups()
|
||||||
assert backups == {TEST_BACKUP.slug: TEST_BACKUP}
|
assert backups == {TEST_BACKUP.backup_id: TEST_BACKUP}
|
||||||
assert agent_errors == {}
|
assert agent_errors == {}
|
||||||
|
|
||||||
|
|
||||||
@@ -181,11 +181,11 @@ async def test_removing_backup(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
|
|
||||||
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
||||||
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP}
|
local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
|
||||||
local_agent._loaded_backups = True
|
local_agent._loaded_backups = True
|
||||||
|
|
||||||
with patch("pathlib.Path.exists", return_value=True):
|
with patch("pathlib.Path.exists", return_value=True):
|
||||||
await manager.async_remove_backup(slug=TEST_LOCAL_BACKUP.slug)
|
await manager.async_remove_backup(TEST_LOCAL_BACKUP.backup_id)
|
||||||
assert "Removed backup located at" in caplog.text
|
assert "Removed backup located at" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ async def test_removing_non_existing_backup(
|
|||||||
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
|
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
|
||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
|
|
||||||
await manager.async_remove_backup(slug="non_existing")
|
await manager.async_remove_backup("non_existing")
|
||||||
assert "Removed backup located at" not in caplog.text
|
assert "Removed backup located at" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@@ -214,18 +214,18 @@ async def test_getting_backup_that_does_not_exist(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
|
|
||||||
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
||||||
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP}
|
local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
|
||||||
local_agent._loaded_backups = True
|
local_agent._loaded_backups = True
|
||||||
|
|
||||||
with patch("pathlib.Path.exists", return_value=False):
|
with patch("pathlib.Path.exists", return_value=False):
|
||||||
backup, agent_errors = await manager.async_get_backup(
|
backup, agent_errors = await manager.async_get_backup(
|
||||||
slug=TEST_LOCAL_BACKUP.slug
|
TEST_LOCAL_BACKUP.backup_id
|
||||||
)
|
)
|
||||||
assert backup is None
|
assert backup is None
|
||||||
assert agent_errors == {}
|
assert agent_errors == {}
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
f"Removing tracked backup ({TEST_LOCAL_BACKUP.slug}) that "
|
f"Removing tracked backup ({TEST_LOCAL_BACKUP.backup_id}) that "
|
||||||
f"does not exists on the expected path {TEST_LOCAL_BACKUP.path}"
|
f"does not exists on the expected path {TEST_LOCAL_BACKUP.path}"
|
||||||
) in caplog.text
|
) in caplog.text
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ async def test_async_create_backup(
|
|||||||
hass, manager, mocked_json_bytes, mocked_tarfile, **params
|
hass, manager, mocked_json_bytes, mocked_tarfile, **params
|
||||||
)
|
)
|
||||||
|
|
||||||
assert "Generated new backup with slug " in caplog.text
|
assert "Generated new backup with backup_id " in caplog.text
|
||||||
assert "Creating backup directory" in caplog.text
|
assert "Creating backup directory" in caplog.text
|
||||||
assert "Loaded 0 platforms" in caplog.text
|
assert "Loaded 0 platforms" in caplog.text
|
||||||
assert "Loaded 1 agents" in caplog.text
|
assert "Loaded 1 agents" in caplog.text
|
||||||
@@ -457,7 +457,7 @@ async def test_async_trigger_restore(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
|
|
||||||
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
||||||
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP}
|
local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
|
||||||
local_agent._loaded_backups = True
|
local_agent._loaded_backups = True
|
||||||
|
|
||||||
with (
|
with (
|
||||||
@@ -466,7 +466,7 @@ async def test_async_trigger_restore(
|
|||||||
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
|
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
|
||||||
):
|
):
|
||||||
await manager.async_restore_backup(
|
await manager.async_restore_backup(
|
||||||
TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password=None
|
TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password=None
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
mocked_write_text.call_args[0][0]
|
mocked_write_text.call_args[0][0]
|
||||||
@@ -486,7 +486,7 @@ async def test_async_trigger_restore_with_password(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
|
|
||||||
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
local_agent = manager.backup_agents[LOCAL_AGENT_ID]
|
||||||
local_agent._backups = {TEST_LOCAL_BACKUP.slug: TEST_LOCAL_BACKUP}
|
local_agent._backups = {TEST_LOCAL_BACKUP.backup_id: TEST_LOCAL_BACKUP}
|
||||||
local_agent._loaded_backups = True
|
local_agent._loaded_backups = True
|
||||||
|
|
||||||
with (
|
with (
|
||||||
@@ -495,7 +495,7 @@ async def test_async_trigger_restore_with_password(
|
|||||||
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
|
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
|
||||||
):
|
):
|
||||||
await manager.async_restore_backup(
|
await manager.async_restore_backup(
|
||||||
slug=TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password="abc123"
|
TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password="abc123"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
mocked_write_text.call_args[0][0]
|
mocked_write_text.call_args[0][0]
|
||||||
@@ -516,5 +516,5 @@ async def test_async_trigger_restore_missing_backup(hass: HomeAssistant) -> None
|
|||||||
|
|
||||||
with pytest.raises(HomeAssistantError, match="Backup abc123 not found"):
|
with pytest.raises(HomeAssistantError, match="Backup abc123 not found"):
|
||||||
await manager.async_restore_backup(
|
await manager.async_restore_backup(
|
||||||
TEST_LOCAL_BACKUP.slug, agent_id=LOCAL_AGENT_ID, password=None
|
TEST_LOCAL_BACKUP.backup_id, agent_id=LOCAL_AGENT_ID, password=None
|
||||||
)
|
)
|
||||||
|
@@ -108,7 +108,9 @@ async def test_details(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
with patch("pathlib.Path.exists", return_value=True):
|
with patch("pathlib.Path.exists", return_value=True):
|
||||||
await client.send_json_auto_id({"type": "backup/details", "slug": "abc123"})
|
await client.send_json_auto_id(
|
||||||
|
{"type": "backup/details", "backup_id": "abc123"}
|
||||||
|
)
|
||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +134,9 @@ async def test_details_with_errors(
|
|||||||
patch("pathlib.Path.exists", return_value=True),
|
patch("pathlib.Path.exists", return_value=True),
|
||||||
patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect),
|
patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect),
|
||||||
):
|
):
|
||||||
await client.send_json_auto_id({"type": "backup/details", "slug": "abc123"})
|
await client.send_json_auto_id(
|
||||||
|
{"type": "backup/details", "backup_id": "abc123"}
|
||||||
|
)
|
||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
|
||||||
|
|
||||||
@@ -158,7 +162,7 @@ async def test_remove(
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.backup.manager.BackupManager.async_remove_backup",
|
"homeassistant.components.backup.manager.BackupManager.async_remove_backup",
|
||||||
):
|
):
|
||||||
await client.send_json_auto_id({"type": "backup/remove", "slug": "abc123"})
|
await client.send_json_auto_id({"type": "backup/remove", "backup_id": "abc123"})
|
||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
|
||||||
|
|
||||||
@@ -281,7 +285,11 @@ async def test_restore(
|
|||||||
"homeassistant.components.backup.manager.BackupManager.async_restore_backup",
|
"homeassistant.components.backup.manager.BackupManager.async_restore_backup",
|
||||||
):
|
):
|
||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{"type": "backup/restore", "slug": "abc123", "agent_id": "backup.local"}
|
{
|
||||||
|
"type": "backup/restore",
|
||||||
|
"backup_id": "abc123",
|
||||||
|
"agent_id": "backup.local",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
|
||||||
@@ -481,15 +489,14 @@ async def test_agents_download(
|
|||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{
|
{
|
||||||
"type": "backup/agents/download",
|
"type": "backup/agents/download",
|
||||||
"slug": "abc123",
|
|
||||||
"agent_id": "domain.test",
|
"agent_id": "domain.test",
|
||||||
"backup_id": "abc123",
|
"backup_id": "abc123",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
with patch.object(BackupAgentTest, "async_download_backup") as download_mock:
|
with patch.object(BackupAgentTest, "async_download_backup") as download_mock:
|
||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
assert download_mock.call_args[0] == ("abc123",)
|
||||||
assert download_mock.call_args[1] == {
|
assert download_mock.call_args[1] == {
|
||||||
"id": "abc123",
|
|
||||||
"path": Path(hass.config.path("tmp_backups"), "abc123.tar"),
|
"path": Path(hass.config.path("tmp_backups"), "abc123.tar"),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +516,6 @@ async def test_agents_download_exception(
|
|||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{
|
{
|
||||||
"type": "backup/agents/download",
|
"type": "backup/agents/download",
|
||||||
"slug": "abc123",
|
|
||||||
"agent_id": "domain.test",
|
"agent_id": "domain.test",
|
||||||
"backup_id": "abc123",
|
"backup_id": "abc123",
|
||||||
}
|
}
|
||||||
@@ -533,7 +539,6 @@ async def test_agents_download_unknown_agent(
|
|||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{
|
{
|
||||||
"type": "backup/agents/download",
|
"type": "backup/agents/download",
|
||||||
"slug": "abc123",
|
|
||||||
"agent_id": "domain.test",
|
"agent_id": "domain.test",
|
||||||
"backup_id": "abc123",
|
"backup_id": "abc123",
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from uuid import UUID
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -69,12 +68,11 @@ async def test_agents_list_backups(
|
|||||||
assert response["result"] == [
|
assert response["result"] == [
|
||||||
{
|
{
|
||||||
"agent_id": "kitchen_sink.syncer",
|
"agent_id": "kitchen_sink.syncer",
|
||||||
|
"backup_id": "abc123",
|
||||||
"date": "1970-01-01T00:00:00Z",
|
"date": "1970-01-01T00:00:00Z",
|
||||||
"id": "def456",
|
|
||||||
"slug": "abc123",
|
|
||||||
"size": 1234,
|
|
||||||
"name": "Kitchen sink syncer",
|
"name": "Kitchen sink syncer",
|
||||||
"protected": False,
|
"protected": False,
|
||||||
|
"size": 1234,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -86,13 +84,11 @@ async def test_agents_download(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test backup agents download."""
|
"""Test backup agents download."""
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
backup_id = "def456"
|
backup_id = "abc123"
|
||||||
slug = "abc123"
|
|
||||||
|
|
||||||
await client.send_json_auto_id(
|
await client.send_json_auto_id(
|
||||||
{
|
{
|
||||||
"type": "backup/agents/download",
|
"type": "backup/agents/download",
|
||||||
"slug": slug,
|
|
||||||
"agent_id": "kitchen_sink.syncer",
|
"agent_id": "kitchen_sink.syncer",
|
||||||
"backup_id": backup_id,
|
"backup_id": backup_id,
|
||||||
}
|
}
|
||||||
@@ -100,7 +96,7 @@ async def test_agents_download(
|
|||||||
response = await client.receive_json()
|
response = await client.receive_json()
|
||||||
|
|
||||||
assert response["success"]
|
assert response["success"]
|
||||||
path = hass.config.path(f"tmp_backups/{slug}.tar")
|
path = hass.config.path(f"tmp_backups/{backup_id}.tar")
|
||||||
assert f"Downloading backup {backup_id} to {path}" in caplog.text
|
assert f"Downloading backup {backup_id} to {path}" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@@ -114,18 +110,16 @@ async def test_agents_upload(
|
|||||||
"""Test backup agents upload."""
|
"""Test backup agents upload."""
|
||||||
ws_client = await hass_ws_client(hass, hass_supervisor_access_token)
|
ws_client = await hass_ws_client(hass, hass_supervisor_access_token)
|
||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
slug = "test-backup"
|
backup_id = "test-backup"
|
||||||
test_backup = BaseBackup(
|
test_backup = BaseBackup(
|
||||||
slug=slug,
|
backup_id=backup_id,
|
||||||
name="Test",
|
|
||||||
date="1970-01-01T00:00:00.000Z",
|
date="1970-01-01T00:00:00.000Z",
|
||||||
size=0.0,
|
name="Test",
|
||||||
protected=False,
|
protected=False,
|
||||||
|
size=0.0,
|
||||||
)
|
)
|
||||||
uuid = UUID(int=123456)
|
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("homeassistant.components.kitchen_sink.backup.uuid4", return_value=uuid),
|
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.backup.manager.BackupManager.async_get_backup",
|
"homeassistant.components.backup.manager.BackupManager.async_get_backup",
|
||||||
) as fetch_backup,
|
) as fetch_backup,
|
||||||
@@ -141,22 +135,20 @@ async def test_agents_upload(
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert resp.status == 201
|
assert resp.status == 201
|
||||||
backup_name = f"{slug}.tar"
|
backup_name = f"{backup_id}.tar"
|
||||||
assert f"Uploading backup {backup_name}" in caplog.text
|
assert f"Uploading backup {backup_name}" in caplog.text
|
||||||
|
|
||||||
with patch("homeassistant.components.kitchen_sink.backup.uuid4", return_value=uuid):
|
await ws_client.send_json_auto_id({"type": "backup/agents/list_backups"})
|
||||||
await ws_client.send_json_auto_id({"type": "backup/agents/list_backups"})
|
response = await ws_client.receive_json()
|
||||||
response = await ws_client.receive_json()
|
|
||||||
|
|
||||||
assert response["success"]
|
assert response["success"]
|
||||||
backup_list = response["result"]
|
backup_list = response["result"]
|
||||||
assert len(backup_list) == 2
|
assert len(backup_list) == 2
|
||||||
assert backup_list[1] == {
|
assert backup_list[1] == {
|
||||||
"agent_id": "kitchen_sink.syncer",
|
"agent_id": "kitchen_sink.syncer",
|
||||||
|
"backup_id": backup_id,
|
||||||
"date": test_backup.date,
|
"date": test_backup.date,
|
||||||
"id": uuid.hex,
|
|
||||||
"slug": slug,
|
|
||||||
"size": 0.0,
|
|
||||||
"name": test_backup.name,
|
"name": test_backup.name,
|
||||||
"protected": test_backup.protected,
|
"protected": test_backup.protected,
|
||||||
|
"size": 0.0,
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user