def _generate_backup_contents( self, tar_file_path: Path, backup_data: dict[str, Any], ) -> None: """Generate backup contents.""" with TemporaryDirectory() as tmp_dir, SecureTarFile( tar_file_path, "w", gzip=False ) as tar_file: tmp_dir_path = Path(tmp_dir) json_util.save_json( tmp_dir_path.joinpath("./backup.json").as_posix(), backup_data, ) with SecureTarFile( tmp_dir_path.joinpath("./homeassistant.tar.gz").as_posix(), "w", ) as core_tar: atomic_contents_add( tar_file=core_tar, origin_path=Path(self.hass.config.path()), excludes=EXCLUDE_FROM_BACKUP, arcname="data", ) tar_file.add(tmp_dir_path, arcname=".")
async def restore_homeassistant(self) -> Awaitable[None]: """Restore Home Assitant Core configuration folder.""" await self.sys_homeassistant.core.stop() # Restore Home Assistant Core config directory homeassistant_file = SecureTarFile(Path(self._tmp.name, "homeassistant.tar.gz"), "r", key=self._key) await self.sys_homeassistant.restore(homeassistant_file) # Generate restore task async def _core_update(): try: if self.homeassistant_version == self.sys_homeassistant.version: return except TypeError: # Home Assistant is not yet installed / None pass except AwesomeVersionCompareException as err: raise BackupError( f"Invalid Home Assistant Core version {self.homeassistant_version}", _LOGGER.error, ) from err await self.sys_homeassistant.core.update(self.homeassistant_version ) return self.sys_create_task(_core_update())
def _folder_save(name: str): """Take backup of a folder.""" slug_name = name.replace("/", "_") tar_name = Path( self._tmp.name, f"{slug_name}.tar{'.gz' if self.compressed else ''}") origin_dir = Path(self.sys_config.path_supervisor, name) # Check if exists if not origin_dir.is_dir(): _LOGGER.warning("Can't find backup folder %s", name) return # Take backup _LOGGER.info("Backing up folder %s", name) with SecureTarFile(tar_name, "w", key=self._key, gzip=self.compressed) as tar_file: atomic_contents_add( tar_file, origin_dir, excludes=[], arcname=".", ) _LOGGER.info("Backup folder %s done", name) self._data[ATTR_FOLDERS].append(name)
def _restore() -> None: try: _LOGGER.info("Restore folder %s", name) with SecureTarFile(tar_name, "r", key=self._key, gzip=self.compressed) as tar_file: tar_file.extractall(path=origin_dir, members=tar_file) _LOGGER.info("Restore folder %s done", name) except (tarfile.TarError, OSError) as err: _LOGGER.warning("Can't restore folder %s: %s", name, err)
async def store_homeassistant(self): """Backup Home Assitant Core configuration folder.""" self._data[ATTR_HOMEASSISTANT] = { ATTR_VERSION: self.sys_homeassistant.version } # Backup Home Assistant Core config directory homeassistant_file = SecureTarFile(Path(self._tmp.name, "homeassistant.tar.gz"), "w", key=self._key) await self.sys_homeassistant.backup(homeassistant_file) # Store size self.homeassistant[ATTR_SIZE] = homeassistant_file.size
async def _addon_restore(addon_slug: str): """Task to restore an add-on into backup.""" tar_name = f"{addon_slug}.tar{'.gz' if self.compressed else ''}" addon_file = SecureTarFile(Path(self._tmp.name, tar_name), "r", key=self._key, gzip=self.compressed) # If exists inside backup if not addon_file.path.exists(): _LOGGER.error("Can't find backup %s", addon_slug) return # Perform a restore try: await self.sys_addons.restore(addon_slug, addon_file) except AddonsError: _LOGGER.error("Can't restore backup %s", addon_slug)
async def test_fixup(coresys: CoreSys, tmp_path): """Test fixup.""" clear_full_backup = FixupSystemClearFullBackup(coresys) assert not clear_full_backup.auto coresys.resolution.suggestions = Suggestion( SuggestionType.CLEAR_FULL_BACKUP, ContextType.SYSTEM) for slug in ["sn1", "sn2", "sn3", "sn4", "sn5"]: temp_tar = Path(tmp_path, f"{slug}.tar") with SecureTarFile(temp_tar, "w"): pass backup = Backup(coresys, temp_tar) backup._data = { # pylint: disable=protected-access ATTR_SLUG: slug, ATTR_DATE: utcnow().isoformat(), ATTR_TYPE: BackupType.PARTIAL if "1" in slug or "5" in slug else BackupType.FULL, } coresys.backups._backups[backup.slug] = backup newest_full_backup = coresys.backups._backups["sn4"] assert newest_full_backup in coresys.backups.list_backups assert (len([ x for x in coresys.backups.list_backups if x.sys_type == BackupType.FULL ]) == 3) await clear_full_backup() assert newest_full_backup in coresys.backups.list_backups assert (len([ x for x in coresys.backups.list_backups if x.sys_type == BackupType.FULL ]) == 1) assert len(coresys.resolution.suggestions) == 0
async def _addon_save(addon: Addon): """Task to store an add-on into backup.""" tar_name = f"{addon.slug}.tar{'.gz' if self.compressed else ''}" addon_file = SecureTarFile(Path(self._tmp.name, tar_name), "w", key=self._key, gzip=self.compressed) # Take backup try: await addon.backup(addon_file) except AddonsError: _LOGGER.error("Can't create backup for %s", addon.slug) return # Store to config self._data[ATTR_ADDONS].append({ ATTR_SLUG: addon.slug, ATTR_NAME: addon.name, ATTR_VERSION: addon.version, ATTR_SIZE: addon_file.size, })