mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[fix] Backup legacy tar
This commit is contained in:
parent
3a6f1bd612
commit
5760e8fe53
4 changed files with 40 additions and 21 deletions
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue