[fix] Backup legacy tar

This commit is contained in:
ljf 2022-10-25 01:22:53 +02:00
parent 3a6f1bd612
commit 5760e8fe53
No known key found for this signature in database
4 changed files with 40 additions and 21 deletions

View file

@ -1050,6 +1050,7 @@ backup:
-p: -p:
full: --prefix full: --prefix
help: Prefix of the backup archive help: Prefix of the backup archive
default: ""
extra: extra:
pattern: &pattern_backup_archive_prefix pattern: &pattern_backup_archive_prefix
- !!str ^[\w\-\._]{1,35}$ - !!str ^[\w\-\._]{1,35}$
@ -1307,7 +1308,7 @@ backup:
help: Keep all archives within this time interval help: Keep all archives within this time interval
extra: extra:
pattern: &pattern_interval pattern: &pattern_interval
- !!str ^\d+[Hdwmy]$ - !!str ^\d+[Hdw]$
- "pattern_interval" - "pattern_interval"

View file

@ -156,11 +156,11 @@ class BorgBackupRepository(LocalBackupRepository):
# password: "XXXXXXXX", # password: "XXXXXXXX",
} }
) )
else: elif self.is_remote:
cmd = ["borg", "delete", self.location] cmd = ["borg", "delete", self.location]
self._call('purge', cmd) self._call('purge', cmd)
if not self.is_remote: else:
super().purge() super().purge()
def list_archives_names(self, prefix=None): def list_archives_names(self, prefix=None):
cmd = ["borg", "list", "--json", self.location] cmd = ["borg", "list", "--json", self.location]

View file

@ -18,19 +18,21 @@
along with this program; if not, see http://www.gnu.org/licenses along with this program; if not, see http://www.gnu.org/licenses
""" """
import glob
import os import os
import tarfile import tarfile
import shutil import shutil
from glob import glob
from datetime import datetime
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.filesystem import free_space_in_directory from yunohost.repository import LocalBackupRepository, BackupArchive
from yunohost.repository import LocalBackupRepository
from yunohost.backup import BackupManager from yunohost.backup import BackupManager
from yunohost.utils.filesystem import space_used_in_directory from yunohost.utils.system import space_used_by_directory, free_space_in_directory
from yunohost.settings import settings_get from yunohost.settings import settings_get
logger = getActionLogger("yunohost.repository") logger = getActionLogger("yunohost.repository")
@ -66,16 +68,13 @@ class TarBackupRepository(LocalBackupRepository):
return [remove_extension(f) for f in archives] return [remove_extension(f) for f in archives]
def compute_space_used(self): def compute_space_used(self):
return space_used_in_directory(self.location) return space_used_by_directory(self.location)
def compute_free_space(self): def compute_free_space(self):
return free_space_in_directory(self.location) return free_space_in_directory(self.location)
def prune(self):
raise NotImplementedError()
class TarBackupArchive(BackupArchive):
class TarBackupArchive:
@property @property
def archive_path(self): def archive_path(self):
@ -166,7 +165,7 @@ class TarBackupArchive:
logger.debug("unable to delete '%s'", backup_file, exc_info=1) logger.debug("unable to delete '%s'", backup_file, exc_info=1)
logger.warning(m18n.n("backup_delete_error", path=backup_file)) logger.warning(m18n.n("backup_delete_error", path=backup_file))
def list(self): def list(self, with_info=False):
try: try:
tar = tarfile.open( tar = tarfile.open(
self.archive_path, self.archive_path,
@ -179,14 +178,28 @@ class TarBackupArchive:
raise YunohostError("backup_archive_open_failed") raise YunohostError("backup_archive_open_failed")
try: try:
return tar.getnames() if not with_info:
return "\n".join(tar.getnames())
else:
return {f.name: {
"mode": f.mode, # FIXME Numeric or letter mode
"type": int(f.type),
"uid": f.uid,
"gid": f.gid,
"user": f.uname,
"group": f.gname,
"size": f.size,
"mtime": datetime.fromtimestamp(f.mtime).isoformat(),
"linktarget": f.linkname,
}
for f in tar.getmembers()}
except (IOError, EOFError, tarfile.ReadError) as e: except (IOError, EOFError, tarfile.ReadError) as e:
tar.close() tar.close()
raise YunohostError( raise YunohostError(
"backup_archive_corrupted", archive=self.archive_path, error=str(e) "backup_archive_corrupted", archive=self.archive_path, error=str(e)
) )
def download(self): def download(self, exclude_paths=[]):
super().download() super().download()
# If symlink, retrieve the real path # If symlink, retrieve the real path
archive_file = self.archive_path archive_file = self.archive_path
@ -206,9 +219,9 @@ class TarBackupArchive:
archive_folder, archive_file_name = archive_file.rsplit("/", 1) archive_folder, archive_file_name = archive_file.rsplit("/", 1)
return static_file(archive_file_name, archive_folder, download=archive_file_name) return static_file(archive_file_name, archive_folder, download=archive_file_name)
def extract(self, paths=None, exclude_paths=[]): def extract(self, paths=[], destination=None, exclude_paths=[]):
paths, exclude_paths = super().extract(paths, exclude_paths) paths, destination, exclude_paths = super().extract(paths, destination, exclude_paths)
# Mount the tarball # Open the tarball
try: try:
tar = tarfile.open( tar = tarfile.open(
self.archive_path, self.archive_path,
@ -228,10 +241,10 @@ class TarBackupArchive:
and all([not tarinfo.name.startswith(path) for path in exclude_paths]) and all([not tarinfo.name.startswith(path) for path in exclude_paths])
) )
] ]
tar.extractall(members=subdir_and_files, path=self.work_dir) tar.extractall(members=subdir_and_files, path=destination)
tar.close() tar.close()
def mount(self): def mount(self, path):
raise NotImplementedError() raise NotImplementedError()
def _archive_exists(self): def _archive_exists(self):

View file

@ -162,6 +162,8 @@ class BackupRepository(ConfigPanel):
return {} return {}
def post_ask__method(self, question): def post_ask__method(self, question):
if question.value:
self.method = question.value
self._cast_by_backup_method() self._cast_by_backup_method()
return {} return {}
@ -351,6 +353,8 @@ class BackupRepository(ConfigPanel):
keep_last -= 1 keep_last -= 1
continue continue
# TODO Improve performances by creating a BackupRepository.delete_archives()
# method to delete several archives in one command
archives[created_at].delete() archives[created_at].delete()
# ================================================= # =================================================
@ -384,6 +388,7 @@ class LocalBackupRepository(BackupRepository):
self.install() self.install()
def purge(self): def purge(self):
# FIXME Manage the case where a repository is inside this repository...
rm(self.location, recursive=True, force=True) rm(self.location, recursive=True, force=True)