mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Add backup agent retention config (#143174)
This commit is contained in:
parent
d9a09a2aea
commit
5392062edd
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass, field, replace
|
from dataclasses import dataclass, field, replace
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -87,12 +88,26 @@ class BackupConfigData:
|
|||||||
else:
|
else:
|
||||||
time = None
|
time = None
|
||||||
days = [Day(day) for day in data["schedule"]["days"]]
|
days = [Day(day) for day in data["schedule"]["days"]]
|
||||||
|
agents = {}
|
||||||
|
for agent_id, agent_data in data["agents"].items():
|
||||||
|
protected = agent_data["protected"]
|
||||||
|
stored_retention = agent_data["retention"]
|
||||||
|
agent_retention: AgentRetentionConfig | None
|
||||||
|
if stored_retention:
|
||||||
|
agent_retention = AgentRetentionConfig(
|
||||||
|
copies=stored_retention["copies"],
|
||||||
|
days=stored_retention["days"],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
agent_retention = None
|
||||||
|
agent_config = AgentConfig(
|
||||||
|
protected=protected,
|
||||||
|
retention=agent_retention,
|
||||||
|
)
|
||||||
|
agents[agent_id] = agent_config
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
agents={
|
agents=agents,
|
||||||
agent_id: AgentConfig(protected=agent_data["protected"])
|
|
||||||
for agent_id, agent_data in data["agents"].items()
|
|
||||||
},
|
|
||||||
automatic_backups_configured=data["automatic_backups_configured"],
|
automatic_backups_configured=data["automatic_backups_configured"],
|
||||||
create_backup=CreateBackupConfig(
|
create_backup=CreateBackupConfig(
|
||||||
agent_ids=data["create_backup"]["agent_ids"],
|
agent_ids=data["create_backup"]["agent_ids"],
|
||||||
@ -176,12 +191,36 @@ class BackupConfig:
|
|||||||
"""Update config."""
|
"""Update config."""
|
||||||
if agents is not UNDEFINED:
|
if agents is not UNDEFINED:
|
||||||
for agent_id, agent_config in agents.items():
|
for agent_id, agent_config in agents.items():
|
||||||
if agent_id not in self.data.agents:
|
agent_retention = agent_config.get("retention")
|
||||||
self.data.agents[agent_id] = AgentConfig(**agent_config)
|
if agent_retention is None:
|
||||||
|
new_agent_retention = None
|
||||||
else:
|
else:
|
||||||
self.data.agents[agent_id] = replace(
|
new_agent_retention = AgentRetentionConfig(
|
||||||
self.data.agents[agent_id], **agent_config
|
copies=agent_retention.get("copies"),
|
||||||
|
days=agent_retention.get("days"),
|
||||||
)
|
)
|
||||||
|
if agent_id not in self.data.agents:
|
||||||
|
old_agent_retention = None
|
||||||
|
self.data.agents[agent_id] = AgentConfig(
|
||||||
|
protected=agent_config.get("protected", False),
|
||||||
|
retention=new_agent_retention,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_agent_config = self.data.agents[agent_id]
|
||||||
|
old_agent_retention = new_agent_config.retention
|
||||||
|
if "protected" in agent_config:
|
||||||
|
new_agent_config = replace(
|
||||||
|
new_agent_config, protected=agent_config["protected"]
|
||||||
|
)
|
||||||
|
if "retention" in agent_config:
|
||||||
|
new_agent_config = replace(
|
||||||
|
new_agent_config, retention=new_agent_retention
|
||||||
|
)
|
||||||
|
self.data.agents[agent_id] = new_agent_config
|
||||||
|
if new_agent_retention != old_agent_retention:
|
||||||
|
# There's a single retention application method
|
||||||
|
# for both global and agent retention settings.
|
||||||
|
self.data.retention.apply(self._manager)
|
||||||
if automatic_backups_configured is not UNDEFINED:
|
if automatic_backups_configured is not UNDEFINED:
|
||||||
self.data.automatic_backups_configured = automatic_backups_configured
|
self.data.automatic_backups_configured = automatic_backups_configured
|
||||||
if create_backup is not UNDEFINED:
|
if create_backup is not UNDEFINED:
|
||||||
@ -207,11 +246,24 @@ class AgentConfig:
|
|||||||
"""Represent the config for an agent."""
|
"""Represent the config for an agent."""
|
||||||
|
|
||||||
protected: bool
|
protected: bool
|
||||||
|
"""Agent protected configuration.
|
||||||
|
|
||||||
|
If True, the agent backups are password protected.
|
||||||
|
"""
|
||||||
|
retention: AgentRetentionConfig | None = None
|
||||||
|
"""Agent retention configuration.
|
||||||
|
|
||||||
|
If None, the global retention configuration is used.
|
||||||
|
If not None, the global retention configuration is ignored for this agent.
|
||||||
|
If an agent retention configuration is set and both copies and days are None,
|
||||||
|
backups will be kept forever for that agent.
|
||||||
|
"""
|
||||||
|
|
||||||
def to_dict(self) -> StoredAgentConfig:
|
def to_dict(self) -> StoredAgentConfig:
|
||||||
"""Convert agent config to a dict."""
|
"""Convert agent config to a dict."""
|
||||||
return {
|
return {
|
||||||
"protected": self.protected,
|
"protected": self.protected,
|
||||||
|
"retention": self.retention.to_dict() if self.retention else None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -219,24 +271,46 @@ class StoredAgentConfig(TypedDict):
|
|||||||
"""Represent the stored config for an agent."""
|
"""Represent the stored config for an agent."""
|
||||||
|
|
||||||
protected: bool
|
protected: bool
|
||||||
|
retention: StoredRetentionConfig | None
|
||||||
|
|
||||||
|
|
||||||
class AgentParametersDict(TypedDict, total=False):
|
class AgentParametersDict(TypedDict, total=False):
|
||||||
"""Represent the parameters for an agent."""
|
"""Represent the parameters for an agent."""
|
||||||
|
|
||||||
protected: bool
|
protected: bool
|
||||||
|
retention: RetentionParametersDict | None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class RetentionConfig:
|
class BaseRetentionConfig:
|
||||||
"""Represent the backup retention configuration."""
|
"""Represent the base backup retention configuration."""
|
||||||
|
|
||||||
copies: int | None = None
|
copies: int | None = None
|
||||||
days: int | None = None
|
days: int | None = None
|
||||||
|
|
||||||
|
def to_dict(self) -> StoredRetentionConfig:
|
||||||
|
"""Convert backup retention configuration to a dict."""
|
||||||
|
return StoredRetentionConfig(
|
||||||
|
copies=self.copies,
|
||||||
|
days=self.days,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class RetentionConfig(BaseRetentionConfig):
|
||||||
|
"""Represent the backup retention configuration."""
|
||||||
|
|
||||||
def apply(self, manager: BackupManager) -> None:
|
def apply(self, manager: BackupManager) -> None:
|
||||||
"""Apply backup retention configuration."""
|
"""Apply backup retention configuration."""
|
||||||
if self.days is not None:
|
agents_retention = {
|
||||||
|
agent_id: agent_config.retention
|
||||||
|
for agent_id, agent_config in manager.config.data.agents.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.days is not None or any(
|
||||||
|
agent_retention and agent_retention.days is not None
|
||||||
|
for agent_retention in agents_retention.values()
|
||||||
|
):
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Scheduling next automatic delete of backups older than %s in 1 day",
|
"Scheduling next automatic delete of backups older than %s in 1 day",
|
||||||
self.days,
|
self.days,
|
||||||
@ -246,13 +320,6 @@ class RetentionConfig:
|
|||||||
LOGGER.debug("Unscheduling next automatic delete")
|
LOGGER.debug("Unscheduling next automatic delete")
|
||||||
self._unschedule_next(manager)
|
self._unschedule_next(manager)
|
||||||
|
|
||||||
def to_dict(self) -> StoredRetentionConfig:
|
|
||||||
"""Convert backup retention configuration to a dict."""
|
|
||||||
return StoredRetentionConfig(
|
|
||||||
copies=self.copies,
|
|
||||||
days=self.days,
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _schedule_next(
|
def _schedule_next(
|
||||||
self,
|
self,
|
||||||
@ -271,17 +338,82 @@ class RetentionConfig:
|
|||||||
"""Return backups older than days to delete."""
|
"""Return backups older than days to delete."""
|
||||||
# we need to check here since we await before
|
# we need to check here since we await before
|
||||||
# this filter is applied
|
# this filter is applied
|
||||||
if self.days is None:
|
agents_retention = {
|
||||||
|
agent_id: agent_config.retention
|
||||||
|
for agent_id, agent_config in manager.config.data.agents.items()
|
||||||
|
}
|
||||||
|
has_agents_retention = any(
|
||||||
|
agent_retention for agent_retention in agents_retention.values()
|
||||||
|
)
|
||||||
|
has_agents_retention_days = any(
|
||||||
|
agent_retention and agent_retention.days is not None
|
||||||
|
for agent_retention in agents_retention.values()
|
||||||
|
)
|
||||||
|
if (global_days := self.days) is None and not has_agents_retention_days:
|
||||||
|
# No global retention days and no agent retention days
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
|
if global_days is not None and not has_agents_retention:
|
||||||
|
# Return early to avoid the longer filtering below.
|
||||||
return {
|
return {
|
||||||
backup_id: backup
|
backup_id: backup
|
||||||
for backup_id, backup in backups.items()
|
for backup_id, backup in backups.items()
|
||||||
if dt_util.parse_datetime(backup.date, raise_on_error=True)
|
if dt_util.parse_datetime(backup.date, raise_on_error=True)
|
||||||
+ timedelta(days=self.days)
|
+ timedelta(days=global_days)
|
||||||
< now
|
< now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If there are any agent retention settings, we need to check
|
||||||
|
# the retention settings, for every backup and agent combination.
|
||||||
|
|
||||||
|
backups_to_delete = {}
|
||||||
|
|
||||||
|
for backup_id, backup in backups.items():
|
||||||
|
backup_date = dt_util.parse_datetime(
|
||||||
|
backup.date, raise_on_error=True
|
||||||
|
)
|
||||||
|
delete_from_agents = set(backup.agents)
|
||||||
|
for agent_id in backup.agents:
|
||||||
|
agent_retention = agents_retention.get(agent_id)
|
||||||
|
if agent_retention is None:
|
||||||
|
# This agent does not have a retention setting,
|
||||||
|
# so the global retention setting should be used.
|
||||||
|
if global_days is None:
|
||||||
|
# This agent does not have a retention setting
|
||||||
|
# and the global retention days setting is None,
|
||||||
|
# so this backup should not be deleted.
|
||||||
|
delete_from_agents.discard(agent_id)
|
||||||
|
continue
|
||||||
|
days = global_days
|
||||||
|
elif (agent_days := agent_retention.days) is None:
|
||||||
|
# This agent has a retention setting
|
||||||
|
# where days is set to None,
|
||||||
|
# so the backup should not be deleted.
|
||||||
|
delete_from_agents.discard(agent_id)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# This agent has a retention setting
|
||||||
|
# where days is set to a number,
|
||||||
|
# so that setting should be used.
|
||||||
|
days = agent_days
|
||||||
|
if backup_date + timedelta(days=days) >= now:
|
||||||
|
# This backup is not older than the retention days,
|
||||||
|
# so this agent should not be deleted.
|
||||||
|
delete_from_agents.discard(agent_id)
|
||||||
|
|
||||||
|
filtered_backup = replace(
|
||||||
|
backup,
|
||||||
|
agents={
|
||||||
|
agent_id: agent_backup_status
|
||||||
|
for agent_id, agent_backup_status in backup.agents.items()
|
||||||
|
if agent_id in delete_from_agents
|
||||||
|
},
|
||||||
|
)
|
||||||
|
backups_to_delete[backup_id] = filtered_backup
|
||||||
|
|
||||||
|
return backups_to_delete
|
||||||
|
|
||||||
await manager.async_delete_filtered_backups(
|
await manager.async_delete_filtered_backups(
|
||||||
include_filter=_automatic_backups_filter, delete_filter=_delete_filter
|
include_filter=_automatic_backups_filter, delete_filter=_delete_filter
|
||||||
)
|
)
|
||||||
@ -312,6 +444,10 @@ class RetentionParametersDict(TypedDict, total=False):
|
|||||||
days: int | None
|
days: int | None
|
||||||
|
|
||||||
|
|
||||||
|
class AgentRetentionConfig(BaseRetentionConfig):
|
||||||
|
"""Represent an agent retention configuration."""
|
||||||
|
|
||||||
|
|
||||||
class StoredBackupSchedule(TypedDict):
|
class StoredBackupSchedule(TypedDict):
|
||||||
"""Represent the stored backup schedule configuration."""
|
"""Represent the stored backup schedule configuration."""
|
||||||
|
|
||||||
@ -554,17 +690,88 @@ async def delete_backups_exceeding_configured_count(manager: BackupManager) -> N
|
|||||||
backups: dict[str, ManagerBackup],
|
backups: dict[str, ManagerBackup],
|
||||||
) -> dict[str, ManagerBackup]:
|
) -> dict[str, ManagerBackup]:
|
||||||
"""Return oldest backups more numerous than copies to delete."""
|
"""Return oldest backups more numerous than copies to delete."""
|
||||||
|
agents_retention = {
|
||||||
|
agent_id: agent_config.retention
|
||||||
|
for agent_id, agent_config in manager.config.data.agents.items()
|
||||||
|
}
|
||||||
|
has_agents_retention = any(
|
||||||
|
agent_retention for agent_retention in agents_retention.values()
|
||||||
|
)
|
||||||
|
has_agents_retention_copies = any(
|
||||||
|
agent_retention and agent_retention.copies is not None
|
||||||
|
for agent_retention in agents_retention.values()
|
||||||
|
)
|
||||||
# we need to check here since we await before
|
# we need to check here since we await before
|
||||||
# this filter is applied
|
# this filter is applied
|
||||||
if manager.config.data.retention.copies is None:
|
if (
|
||||||
|
global_copies := manager.config.data.retention.copies
|
||||||
|
) is None and not has_agents_retention_copies:
|
||||||
|
# No global retention copies and no agent retention copies
|
||||||
return {}
|
return {}
|
||||||
|
if global_copies is not None and not has_agents_retention:
|
||||||
|
# Return early to avoid the longer filtering below.
|
||||||
return dict(
|
return dict(
|
||||||
sorted(
|
sorted(
|
||||||
backups.items(),
|
backups.items(),
|
||||||
key=lambda backup_item: backup_item[1].date,
|
key=lambda backup_item: backup_item[1].date,
|
||||||
)[: max(len(backups) - manager.config.data.retention.copies, 0)]
|
)[: max(len(backups) - global_copies, 0)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
backups_by_agent: dict[str, dict[str, ManagerBackup]] = defaultdict(dict)
|
||||||
|
for backup_id, backup in backups.items():
|
||||||
|
for agent_id in backup.agents:
|
||||||
|
backups_by_agent[agent_id][backup_id] = backup
|
||||||
|
|
||||||
|
backups_to_delete_by_agent: dict[str, dict[str, ManagerBackup]] = defaultdict(
|
||||||
|
dict
|
||||||
|
)
|
||||||
|
for agent_id, agent_backups in backups_by_agent.items():
|
||||||
|
agent_retention = agents_retention.get(agent_id)
|
||||||
|
if agent_retention is None:
|
||||||
|
# This agent does not have a retention setting,
|
||||||
|
# so the global retention setting should be used.
|
||||||
|
if global_copies is None:
|
||||||
|
# This agent does not have a retention setting
|
||||||
|
# and the global retention copies setting is None,
|
||||||
|
# so backups should not be deleted.
|
||||||
|
continue
|
||||||
|
# The global retention setting will be used.
|
||||||
|
copies = global_copies
|
||||||
|
elif (agent_copies := agent_retention.copies) is None:
|
||||||
|
# This agent has a retention setting
|
||||||
|
# where copies is set to None,
|
||||||
|
# so backups should not be deleted.
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# This agent retention setting will be used.
|
||||||
|
copies = agent_copies
|
||||||
|
|
||||||
|
backups_to_delete_by_agent[agent_id] = dict(
|
||||||
|
sorted(
|
||||||
|
agent_backups.items(),
|
||||||
|
key=lambda backup_item: backup_item[1].date,
|
||||||
|
)[: max(len(agent_backups) - copies, 0)]
|
||||||
|
)
|
||||||
|
|
||||||
|
backup_ids_to_delete: dict[str, set[str]] = defaultdict(set)
|
||||||
|
for agent_id, to_delete in backups_to_delete_by_agent.items():
|
||||||
|
for backup_id in to_delete:
|
||||||
|
backup_ids_to_delete[backup_id].add(agent_id)
|
||||||
|
backups_to_delete: dict[str, ManagerBackup] = {}
|
||||||
|
for backup_id, agent_ids in backup_ids_to_delete.items():
|
||||||
|
backup = backups[backup_id]
|
||||||
|
# filter the backup to only include the agents that should be deleted
|
||||||
|
filtered_backup = replace(
|
||||||
|
backup,
|
||||||
|
agents={
|
||||||
|
agent_id: agent_backup_status
|
||||||
|
for agent_id, agent_backup_status in backup.agents.items()
|
||||||
|
if agent_id in agent_ids
|
||||||
|
},
|
||||||
|
)
|
||||||
|
backups_to_delete[backup_id] = filtered_backup
|
||||||
|
return backups_to_delete
|
||||||
|
|
||||||
await manager.async_delete_filtered_backups(
|
await manager.async_delete_filtered_backups(
|
||||||
include_filter=_automatic_backups_filter, delete_filter=_delete_filter
|
include_filter=_automatic_backups_filter, delete_filter=_delete_filter
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|||||||
STORE_DELAY_SAVE = 30
|
STORE_DELAY_SAVE = 30
|
||||||
STORAGE_KEY = DOMAIN
|
STORAGE_KEY = DOMAIN
|
||||||
STORAGE_VERSION = 1
|
STORAGE_VERSION = 1
|
||||||
STORAGE_VERSION_MINOR = 5
|
STORAGE_VERSION_MINOR = 6
|
||||||
|
|
||||||
|
|
||||||
class StoredBackupData(TypedDict):
|
class StoredBackupData(TypedDict):
|
||||||
@ -72,6 +72,10 @@ class _BackupStore(Store[StoredBackupData]):
|
|||||||
data["config"]["automatic_backups_configured"] = (
|
data["config"]["automatic_backups_configured"] = (
|
||||||
data["config"]["create_backup"]["password"] is not None
|
data["config"]["create_backup"]["password"] is not None
|
||||||
)
|
)
|
||||||
|
if old_minor_version < 6:
|
||||||
|
# Version 1.6 adds agent retention settings
|
||||||
|
for agent in data["config"]["agents"]:
|
||||||
|
data["config"]["agents"][agent]["retention"] = None
|
||||||
|
|
||||||
# Note: We allow reading data with major version 2.
|
# Note: We allow reading data with major version 2.
|
||||||
# Reject if major version is higher than 2.
|
# Reject if major version is higher than 2.
|
||||||
|
@ -346,7 +346,28 @@ async def handle_config_info(
|
|||||||
@websocket_api.websocket_command(
|
@websocket_api.websocket_command(
|
||||||
{
|
{
|
||||||
vol.Required("type"): "backup/config/update",
|
vol.Required("type"): "backup/config/update",
|
||||||
vol.Optional("agents"): vol.Schema({str: {"protected": bool}}),
|
vol.Optional("agents"): vol.Schema(
|
||||||
|
{
|
||||||
|
str: {
|
||||||
|
vol.Optional("protected"): bool,
|
||||||
|
vol.Optional("retention"): vol.Any(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
# Note: We can't use cv.positive_int because it allows 0 even
|
||||||
|
# though 0 is not positive.
|
||||||
|
vol.Optional("copies"): vol.Any(
|
||||||
|
vol.All(int, vol.Range(min=1)), None
|
||||||
|
),
|
||||||
|
vol.Optional("days"): vol.Any(
|
||||||
|
vol.All(int, vol.Range(min=1)), None
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
vol.Optional("automatic_backups_configured"): bool,
|
vol.Optional("automatic_backups_configured"): bool,
|
||||||
vol.Optional("create_backup"): vol.Schema(
|
vol.Optional("create_backup"): vol.Schema(
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -86,7 +86,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -131,7 +131,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -177,7 +177,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -196,6 +196,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -225,7 +226,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -244,6 +245,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -274,7 +276,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -293,6 +295,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -322,7 +325,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -341,6 +344,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -371,7 +375,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -390,6 +394,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': True,
|
'automatic_backups_configured': True,
|
||||||
@ -419,7 +424,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -438,6 +443,7 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test.remote': dict({
|
'test.remote': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': True,
|
'automatic_backups_configured': True,
|
||||||
@ -468,7 +474,112 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
|
'version': 1,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_store_migration[store_data5]
|
||||||
|
dict({
|
||||||
|
'data': dict({
|
||||||
|
'backups': list([
|
||||||
|
dict({
|
||||||
|
'backup_id': 'abc123',
|
||||||
|
'failed_agent_ids': list([
|
||||||
|
'test.remote',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test.remote': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': True,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': 'hunter2',
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'state': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'key': 'backup',
|
||||||
|
'minor_version': 6,
|
||||||
|
'version': 1,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_store_migration[store_data5].1
|
||||||
|
dict({
|
||||||
|
'data': dict({
|
||||||
|
'backups': list([
|
||||||
|
dict({
|
||||||
|
'backup_id': 'abc123',
|
||||||
|
'failed_agent_ids': list([
|
||||||
|
'test.remote',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test.remote': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': True,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
'test-agent',
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': 'hunter2',
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'state': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'key': 'backup',
|
||||||
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
@ -300,6 +300,61 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_config_load_config_info[with_hassio-storage_data10]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test-agent1': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': 3,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'test-agent2': dict({
|
||||||
|
'protected': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
'test-agent',
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': False,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': '2024-11-17T04:55:00+01:00',
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
'mon',
|
||||||
|
'sun',
|
||||||
|
]),
|
||||||
|
'recurrence': 'custom_days',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_config_load_config_info[with_hassio-storage_data1]
|
# name: test_config_load_config_info[with_hassio-storage_data1]
|
||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
@ -556,9 +611,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -714,6 +771,61 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_config_load_config_info[without_hassio-storage_data10]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test-agent1': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': 3,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'test-agent2': dict({
|
||||||
|
'protected': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
'test-agent',
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': False,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': '2024-11-17T04:55:00+01:00',
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
'mon',
|
||||||
|
'sun',
|
||||||
|
]),
|
||||||
|
'recurrence': 'custom_days',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_config_load_config_info[without_hassio-storage_data1]
|
# name: test_config_load_config_info[without_hassio-storage_data1]
|
||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
@ -966,9 +1078,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1198,7 +1312,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1315,7 +1429,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1432,7 +1546,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1482,9 +1596,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1527,9 +1643,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1559,7 +1677,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1609,9 +1727,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1653,9 +1773,11 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1690,6 +1812,104 @@
|
|||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_config_update[commands13].3
|
# name: test_config_update[commands13].3
|
||||||
|
dict({
|
||||||
|
'id': 7,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test-agent1': dict({
|
||||||
|
'protected': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': 3,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'test-agent2': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_config_update[commands13].4
|
||||||
|
dict({
|
||||||
|
'id': 9,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
'test-agent1': dict({
|
||||||
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
|
}),
|
||||||
|
'test-agent2': dict({
|
||||||
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': 7,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_config_update[commands13].5
|
||||||
dict({
|
dict({
|
||||||
'data': dict({
|
'data': dict({
|
||||||
'backups': list([
|
'backups': list([
|
||||||
@ -1698,9 +1918,14 @@
|
|||||||
'agents': dict({
|
'agents': dict({
|
||||||
'test-agent1': dict({
|
'test-agent1': dict({
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
'retention': None,
|
||||||
}),
|
}),
|
||||||
'test-agent2': dict({
|
'test-agent2': dict({
|
||||||
'protected': True,
|
'protected': True,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': 7,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'automatic_backups_configured': False,
|
'automatic_backups_configured': False,
|
||||||
@ -1730,7 +1955,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1845,7 +2070,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -1960,7 +2185,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2077,7 +2302,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2196,7 +2421,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2313,7 +2538,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2434,7 +2659,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2559,7 +2784,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2676,7 +2901,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2793,7 +3018,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -2910,7 +3135,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -3027,7 +3252,7 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'key': 'backup',
|
'key': 'backup',
|
||||||
'minor_version': 5,
|
'minor_version': 6,
|
||||||
'version': 1,
|
'version': 1,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
@ -3259,6 +3484,158 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_config_update_errors[command12]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_config_update_errors[command12].1
|
||||||
|
dict({
|
||||||
|
'id': 3,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_config_update_errors[command13]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_config_update_errors[command13].1
|
||||||
|
dict({
|
||||||
|
'id': 3,
|
||||||
|
'result': dict({
|
||||||
|
'config': dict({
|
||||||
|
'agents': dict({
|
||||||
|
}),
|
||||||
|
'automatic_backups_configured': False,
|
||||||
|
'create_backup': dict({
|
||||||
|
'agent_ids': list([
|
||||||
|
]),
|
||||||
|
'include_addons': None,
|
||||||
|
'include_all_addons': False,
|
||||||
|
'include_database': True,
|
||||||
|
'include_folders': None,
|
||||||
|
'name': None,
|
||||||
|
'password': None,
|
||||||
|
}),
|
||||||
|
'last_attempted_automatic_backup': None,
|
||||||
|
'last_completed_automatic_backup': None,
|
||||||
|
'next_automatic_backup': None,
|
||||||
|
'next_automatic_backup_additional': False,
|
||||||
|
'retention': dict({
|
||||||
|
'copies': None,
|
||||||
|
'days': None,
|
||||||
|
}),
|
||||||
|
'schedule': dict({
|
||||||
|
'days': list([
|
||||||
|
]),
|
||||||
|
'recurrence': 'never',
|
||||||
|
'time': None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_config_update_errors[command1]
|
# name: test_config_update_errors[command1]
|
||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
|
@ -98,7 +98,7 @@ def mock_delay_save() -> Generator[None]:
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"config": {
|
"config": {
|
||||||
"agents": {"test.remote": {"protected": True}},
|
"agents": {"test.remote": {"protected": True, "retention": None}},
|
||||||
"automatic_backups_configured": False,
|
"automatic_backups_configured": False,
|
||||||
"create_backup": {
|
"create_backup": {
|
||||||
"agent_ids": [],
|
"agent_ids": [],
|
||||||
@ -200,6 +200,49 @@ def mock_delay_save() -> Generator[None]:
|
|||||||
"minor_version": 4,
|
"minor_version": 4,
|
||||||
"version": 1,
|
"version": 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"backups": [
|
||||||
|
{
|
||||||
|
"backup_id": "abc123",
|
||||||
|
"failed_agent_ids": ["test.remote"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"agents": {
|
||||||
|
"test.remote": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {"copies": None, "days": None},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"automatic_backups_configured": True,
|
||||||
|
"create_backup": {
|
||||||
|
"agent_ids": [],
|
||||||
|
"include_addons": None,
|
||||||
|
"include_all_addons": False,
|
||||||
|
"include_database": True,
|
||||||
|
"include_folders": None,
|
||||||
|
"name": None,
|
||||||
|
"password": "hunter2",
|
||||||
|
},
|
||||||
|
"last_attempted_automatic_backup": None,
|
||||||
|
"last_completed_automatic_backup": None,
|
||||||
|
"retention": {
|
||||||
|
"copies": None,
|
||||||
|
"days": None,
|
||||||
|
},
|
||||||
|
"schedule": {
|
||||||
|
"days": [],
|
||||||
|
"recurrence": "never",
|
||||||
|
"state": "never",
|
||||||
|
"time": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": DOMAIN,
|
||||||
|
"minor_version": 6,
|
||||||
|
"version": 1,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_store_migration(
|
async def test_store_migration(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Tests for the Backup integration."""
|
"""Tests for the Backup integration."""
|
||||||
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
|
from dataclasses import replace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import ANY, AsyncMock, MagicMock, Mock, call, patch
|
from unittest.mock import ANY, AsyncMock, MagicMock, Mock, call, patch
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ import pytest
|
|||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.backup import (
|
from homeassistant.components.backup import (
|
||||||
|
AddonInfo,
|
||||||
AgentBackup,
|
AgentBackup,
|
||||||
BackupAgentError,
|
BackupAgentError,
|
||||||
BackupNotFound,
|
BackupNotFound,
|
||||||
@ -81,6 +83,21 @@ DEFAULT_STORAGE_DATA: dict[str, Any] = {
|
|||||||
}
|
}
|
||||||
DAILY = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
|
DAILY = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
|
||||||
|
|
||||||
|
TEST_MANAGER_BACKUP = ManagerBackup(
|
||||||
|
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
|
||||||
|
agents={"test.test-agent": AgentBackupStatus(protected=True, size=0)},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="1970-01-01T00:00:00.000Z",
|
||||||
|
database_included=True,
|
||||||
|
extra_metadata={"instance_id": "abc123", "with_automatic_settings": True},
|
||||||
|
folders=[Folder.MEDIA, Folder.SHARE],
|
||||||
|
homeassistant_included=True,
|
||||||
|
homeassistant_version="2024.12.0",
|
||||||
|
name="Test",
|
||||||
|
failed_agent_ids=[],
|
||||||
|
with_automatic_settings=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def sync_access_token_proxy(
|
def sync_access_token_proxy(
|
||||||
@ -1160,8 +1177,8 @@ async def test_agents_info(
|
|||||||
"backups": [],
|
"backups": [],
|
||||||
"config": {
|
"config": {
|
||||||
"agents": {
|
"agents": {
|
||||||
"test-agent1": {"protected": True},
|
"test-agent1": {"protected": True, "retention": None},
|
||||||
"test-agent2": {"protected": False},
|
"test-agent2": {"protected": False, "retention": None},
|
||||||
},
|
},
|
||||||
"automatic_backups_configured": False,
|
"automatic_backups_configured": False,
|
||||||
"create_backup": {
|
"create_backup": {
|
||||||
@ -1253,6 +1270,47 @@ async def test_agents_info(
|
|||||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"backup": {
|
||||||
|
"data": {
|
||||||
|
"backups": [],
|
||||||
|
"config": {
|
||||||
|
"agents": {
|
||||||
|
"test-agent1": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {"copies": 3, "days": None},
|
||||||
|
},
|
||||||
|
"test-agent2": {
|
||||||
|
"protected": False,
|
||||||
|
"retention": {"copies": None, "days": 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"automatic_backups_configured": False,
|
||||||
|
"create_backup": {
|
||||||
|
"agent_ids": ["test-agent"],
|
||||||
|
"include_addons": None,
|
||||||
|
"include_all_addons": False,
|
||||||
|
"include_database": False,
|
||||||
|
"include_folders": None,
|
||||||
|
"name": None,
|
||||||
|
"password": None,
|
||||||
|
},
|
||||||
|
"retention": {"copies": None, "days": None},
|
||||||
|
"last_attempted_automatic_backup": None,
|
||||||
|
"last_completed_automatic_backup": None,
|
||||||
|
"schedule": {
|
||||||
|
"days": ["mon", "sun"],
|
||||||
|
"recurrence": "custom_days",
|
||||||
|
"state": "never",
|
||||||
|
"time": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"key": DOMAIN,
|
||||||
|
"version": store.STORAGE_VERSION,
|
||||||
|
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -1271,7 +1329,7 @@ async def test_config_load_config_info(
|
|||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
hass_storage: dict[str, Any],
|
hass_storage: dict[str, Any],
|
||||||
with_hassio: bool,
|
with_hassio: bool,
|
||||||
storage_data: dict[str, Any] | None,
|
storage_data: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test loading stored backup config and reading it via config/info."""
|
"""Test loading stored backup config and reading it via config/info."""
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
@ -1412,6 +1470,20 @@ async def test_config_load_config_info(
|
|||||||
"test-agent2": {"protected": True},
|
"test-agent2": {"protected": True},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test-agent1": {"retention": {"copies": 3}},
|
||||||
|
"test-agent2": {"retention": None},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test-agent1": {"retention": None},
|
||||||
|
"test-agent2": {"retention": {"days": 7}},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -1433,7 +1505,7 @@ async def test_config_update(
|
|||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
commands: dict[str, Any],
|
commands: list[dict[str, Any]],
|
||||||
hass_storage: dict[str, Any],
|
hass_storage: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test updating the backup config."""
|
"""Test updating the backup config."""
|
||||||
@ -1522,6 +1594,14 @@ async def test_config_update(
|
|||||||
"type": "backup/config/update",
|
"type": "backup/config/update",
|
||||||
"retention": {"days": 0},
|
"retention": {"days": 0},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {"test-agent1": {"retention": {"copies": 0}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {"test-agent1": {"retention": {"days": 0}}},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_config_update_errors(
|
async def test_config_update_errors(
|
||||||
@ -2489,6 +2569,253 @@ async def test_config_schedule_logic(
|
|||||||
1,
|
1,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {
|
||||||
|
"copies": 1,
|
||||||
|
"days": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||||
|
"retention": {"copies": 3, "days": None},
|
||||||
|
"schedule": {"recurrence": "daily"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-5": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-5",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent": [call("backup-1")],
|
||||||
|
"test.test-agent2": [call("backup-1"), call("backup-2")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {
|
||||||
|
"copies": 1,
|
||||||
|
"days": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||||
|
"retention": {"copies": None, "days": None},
|
||||||
|
"schedule": {"recurrence": "daily"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-5": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-5",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent2": [call("backup-1"), call("backup-2")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {
|
||||||
|
"copies": None,
|
||||||
|
"days": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||||
|
"retention": {"copies": 2, "days": None},
|
||||||
|
"schedule": {"recurrence": "daily"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-5": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-5",
|
||||||
|
date="2024-11-12T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
"2024-11-12T04:45:00+01:00",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent2": [call("backup-1")],
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@patch("homeassistant.components.backup.config.BACKUP_START_TIME_JITTER", 0)
|
@patch("homeassistant.components.backup.config.BACKUP_START_TIME_JITTER", 0)
|
||||||
@ -3221,6 +3548,223 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||||||
1,
|
1,
|
||||||
{"test.test-agent": [call("backup-1"), call("backup-2")]},
|
{"test.test-agent": [call("backup-1"), call("backup-2")]},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {"days": 3},
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test-agent"]},
|
||||||
|
"retention": {"copies": None, "days": 2},
|
||||||
|
"schedule": {"recurrence": "never"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-11T12:00:00+01:00",
|
||||||
|
"2024-11-12T12:00:00+01:00",
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent": [call("backup-1")],
|
||||||
|
"test.test-agent2": [call("backup-1"), call("backup-2")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {"days": 3},
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test-agent"]},
|
||||||
|
"retention": {"copies": None, "days": None},
|
||||||
|
"schedule": {"recurrence": "never"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-11T12:00:00+01:00",
|
||||||
|
"2024-11-12T12:00:00+01:00",
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent": [call("backup-1")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "backup/config/update",
|
||||||
|
"agents": {
|
||||||
|
"test.test-agent": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": None,
|
||||||
|
},
|
||||||
|
"test.test-agent2": {
|
||||||
|
"protected": True,
|
||||||
|
"retention": {"copies": None, "days": None},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"create_backup": {"agent_ids": ["test-agent"]},
|
||||||
|
"retention": {"copies": None, "days": 2},
|
||||||
|
"schedule": {"recurrence": "never"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"backup-1": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-1",
|
||||||
|
date="2024-11-09T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-2": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-2",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-3": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-3",
|
||||||
|
date="2024-11-11T04:45:00+01:00",
|
||||||
|
with_automatic_settings=True,
|
||||||
|
),
|
||||||
|
"backup-4": replace(
|
||||||
|
TEST_MANAGER_BACKUP,
|
||||||
|
agents={
|
||||||
|
"test.test-agent": MagicMock(spec=AgentBackupStatus),
|
||||||
|
"test.test-agent2": MagicMock(spec=AgentBackupStatus),
|
||||||
|
},
|
||||||
|
backup_id="backup-4",
|
||||||
|
date="2024-11-10T04:45:00+01:00",
|
||||||
|
with_automatic_settings=False,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
"2024-11-11T04:45:00+01:00",
|
||||||
|
"2024-11-11T12:00:00+01:00",
|
||||||
|
"2024-11-12T12:00:00+01:00",
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"test.test-agent": [call("backup-1"), call("backup-2")],
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_config_retention_days_logic(
|
async def test_config_retention_days_logic(
|
||||||
@ -3278,7 +3822,7 @@ async def test_config_retention_days_logic(
|
|||||||
freezer.move_to(start_time)
|
freezer.move_to(start_time)
|
||||||
|
|
||||||
mock_agents = await setup_backup_integration(
|
mock_agents = await setup_backup_integration(
|
||||||
hass, remote_agents=["test.test-agent"]
|
hass, remote_agents=["test.test-agent", "test.test-agent2"]
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user