rework backup

This commit is contained in:
Kay0u 2020-04-12 23:28:49 +02:00
parent c33e61ab2e
commit e63679684a
No known key found for this signature in database
GPG key ID: AE1DCADB6415A156

View file

@ -752,7 +752,7 @@ class BackupManager():
for method in self.methods: for method in self.methods:
logger.debug(m18n.n('backup_applying_method_' + method.method_name)) logger.debug(m18n.n('backup_applying_method_' + method.method_name))
method.mount_and_backup(self) method.mount_and_backup()
logger.debug(m18n.n('backup_method_' + method.method_name + '_finished')) logger.debug(m18n.n('backup_method_' + method.method_name + '_finished'))
def _compute_backup_size(self): def _compute_backup_size(self):
@ -851,7 +851,7 @@ class RestoreManager():
self.info = backup_info(name, with_details=True) self.info = backup_info(name, with_details=True)
self.archive_path = self.info['path'] self.archive_path = self.info['path']
self.name = name self.name = name
self.method = BackupMethod.create(method) self.method = BackupMethod.create(method, self)
self.targets = BackupRestoreTargetsManager() self.targets = BackupRestoreTargetsManager()
# #
@ -956,6 +956,9 @@ class RestoreManager():
# These are the hooks on the current installation # These are the hooks on the current installation
available_restore_system_hooks = hook_list("restore")["hooks"] available_restore_system_hooks = hook_list("restore")["hooks"]
custom_restore_hook_folder = os.path.join(CUSTOM_HOOK_FOLDER, 'restore')
filesystem.mkdir(custom_restore_hook_folder, 755, parents=True, force=True)
for system_part in target_list: for system_part in target_list:
# By default, we'll use the restore hooks on the current install # By default, we'll use the restore hooks on the current install
# if available # if available
@ -967,24 +970,23 @@ class RestoreManager():
continue continue
# Otherwise, attempt to find it (or them?) in the archive # Otherwise, attempt to find it (or them?) in the archive
hook_paths = '{:s}/hooks/restore/*-{:s}'.format(self.work_dir, system_part)
hook_paths = glob(hook_paths)
# If we didn't find it, we ain't gonna be able to restore it # If we didn't find it, we ain't gonna be able to restore it
if len(hook_paths) == 0: if system_part not in self.info['system'] or len(self.info['system'][system_part]['paths']) == 0:
logger.exception(m18n.n('restore_hook_unavailable', part=system_part)) logger.exception(m18n.n('restore_hook_unavailable', part=system_part))
self.targets.set_result("system", system_part, "Skipped") self.targets.set_result("system", system_part, "Skipped")
continue continue
hook_paths = self.info['system'][system_part]['paths']
hook_paths = [ 'hooks/restore/%s' % os.path.basename(p) for p in hook_paths ]
# Otherwise, add it from the archive to the system # Otherwise, add it from the archive to the system
# FIXME: Refactor hook_add and use it instead # FIXME: Refactor hook_add and use it instead
custom_restore_hook_folder = os.path.join(CUSTOM_HOOK_FOLDER, 'restore')
filesystem.mkdir(custom_restore_hook_folder, 755, True)
for hook_path in hook_paths: for hook_path in hook_paths:
logger.debug("Adding restoration script '%s' to the system " logger.debug("Adding restoration script '%s' to the system "
"from the backup archive '%s'", hook_path, "from the backup archive '%s'", hook_path,
self.archive_path) self.archive_path)
shutil.copy(hook_path, custom_restore_hook_folder) self.method.copy(hook_path, custom_restore_hook_folder)
def set_apps_targets(self, apps=[]): def set_apps_targets(self, apps=[]):
""" """
@ -1044,7 +1046,7 @@ class RestoreManager():
filesystem.mkdir(self.work_dir, parents=True) filesystem.mkdir(self.work_dir, parents=True)
self.method.mount(self) self.method.mount()
self._read_info_files() self._read_info_files()
@ -1499,19 +1501,19 @@ class BackupMethod(object):
method_name method_name
Public methods: Public methods:
mount_and_backup(self, backup_manager) mount_and_backup(self)
mount(self, restore_manager) mount(self)
create(cls, method, **kwargs) create(cls, method, **kwargs)
Usage: Usage:
method = BackupMethod.create("tar") method = BackupMethod.create("tar")
method.mount_and_backup(backup_manager) method.mount_and_backup()
#or #or
method = BackupMethod.create("copy") method = BackupMethod.create("copy")
method.mount(restore_manager) method.mount(restore_manager)
""" """
def __init__(self, repo=None): def __init__(self, manager, repo=None):
""" """
BackupMethod constructors BackupMethod constructors
@ -1524,6 +1526,7 @@ class BackupMethod(object):
BackupRepository object. If None, the default repo is used : BackupRepository object. If None, the default repo is used :
/home/yunohost.backup/archives/ /home/yunohost.backup/archives/
""" """
self.manager = manager
self.repo = ARCHIVES_PATH if repo is None else repo self.repo = ARCHIVES_PATH if repo is None else repo
@property @property
@ -1569,18 +1572,13 @@ class BackupMethod(object):
""" """
return False return False
def mount_and_backup(self, backup_manager): def mount_and_backup(self):
""" """
Run the backup on files listed by the BackupManager instance Run the backup on files listed by the BackupManager instance
This method shouldn't be overrided, prefer overriding self.backup() and This method shouldn't be overrided, prefer overriding self.backup() and
self.clean() self.clean()
Args:
backup_manager -- (BackupManager) A backup manager instance that has
already done the files collection step.
""" """
self.manager = backup_manager
if self.need_mount(): if self.need_mount():
self._organize_files() self._organize_files()
@ -1589,17 +1587,13 @@ class BackupMethod(object):
finally: finally:
self.clean() self.clean()
def mount(self, restore_manager): def mount(self):
""" """
Mount the archive from RestoreManager instance in the working directory Mount the archive from RestoreManager instance in the working directory
This method should be extended. This method should be extended.
Args:
restore_manager -- (RestoreManager) A restore manager instance
contains an archive to restore.
""" """
self.manager = restore_manager pass
def clean(self): def clean(self):
""" """
@ -1781,8 +1775,8 @@ class CopyBackupMethod(BackupMethod):
could be the inverse for restoring could be the inverse for restoring
""" """
def __init__(self, repo=None): def __init__(self, manager, repo=None):
super(CopyBackupMethod, self).__init__(repo) super(CopyBackupMethod, self).__init__(manager, repo)
@property @property
def method_name(self): def method_name(self):
@ -1836,6 +1830,9 @@ class CopyBackupMethod(BackupMethod):
"&&", "umount", "-R", self.work_dir]) "&&", "umount", "-R", self.work_dir])
raise YunohostError('backup_cant_mount_uncompress_archive') raise YunohostError('backup_cant_mount_uncompress_archive')
def copy(self, file, target):
shutil.copy(file, target)
class TarBackupMethod(BackupMethod): class TarBackupMethod(BackupMethod):
@ -1843,8 +1840,8 @@ class TarBackupMethod(BackupMethod):
This class compress all files to backup in archive. This class compress all files to backup in archive.
""" """
def __init__(self, repo=None): def __init__(self, manager, repo=None):
super(TarBackupMethod, self).__init__(repo) super(TarBackupMethod, self).__init__(manager, repo)
@property @property
def method_name(self): def method_name(self):
@ -1904,7 +1901,7 @@ class TarBackupMethod(BackupMethod):
if not os.path.isfile(link): if not os.path.isfile(link):
os.symlink(self._archive_file, link) os.symlink(self._archive_file, link)
def mount(self, restore_manager): def mount(self):
""" """
Mount the archive. We avoid copy to be able to restore on system without Mount the archive. We avoid copy to be able to restore on system without
too many space. too many space.
@ -1914,7 +1911,7 @@ class TarBackupMethod(BackupMethod):
backup_archive_corrupted -- Raised if the archive appears corrupted backup_archive_corrupted -- Raised if the archive appears corrupted
backup_archive_cant_retrieve_info_json -- If the info.json file can't be retrieved backup_archive_cant_retrieve_info_json -- If the info.json file can't be retrieved
""" """
super(TarBackupMethod, self).mount(restore_manager) super(TarBackupMethod, self).mount()
# Check the archive can be open # Check the archive can be open
try: try:
@ -1994,6 +1991,11 @@ class TarBackupMethod(BackupMethod):
# FIXME : Don't we want to close the tar archive here or at some point ? # FIXME : Don't we want to close the tar archive here or at some point ?
def copy(self, file, target):
tar = tarfile.open(self._archive_file, "r:gz")
tar.extract(file, path=target)
tar.close()
class BorgBackupMethod(BackupMethod): class BorgBackupMethod(BackupMethod):
@ -2011,6 +2013,9 @@ class BorgBackupMethod(BackupMethod):
def mount(self, mnt_path): def mount(self, mnt_path):
raise YunohostError('backup_borg_not_implemented') raise YunohostError('backup_borg_not_implemented')
def copy(self, file, target):
raise YunohostError('backup_borg_not_implemented')
class CustomBackupMethod(BackupMethod): class CustomBackupMethod(BackupMethod):
@ -2020,8 +2025,8 @@ class CustomBackupMethod(BackupMethod):
/etc/yunohost/hooks.d/backup_method/ /etc/yunohost/hooks.d/backup_method/
""" """
def __init__(self, repo=None, method=None, **kwargs): def __init__(self, manager, repo=None, method=None, **kwargs):
super(CustomBackupMethod, self).__init__(repo) super(CustomBackupMethod, self).__init__(manager, repo)
self.args = kwargs self.args = kwargs
self.method = method self.method = method
self._need_mount = None self._need_mount = None
@ -2062,14 +2067,14 @@ class CustomBackupMethod(BackupMethod):
if ret_failed: if ret_failed:
raise YunohostError('backup_custom_backup_error') raise YunohostError('backup_custom_backup_error')
def mount(self, restore_manager): def mount(self):
""" """
Launch a custom script to mount the custom archive Launch a custom script to mount the custom archive
Exceptions: Exceptions:
backup_custom_mount_error -- Raised if the custom script failed backup_custom_mount_error -- Raised if the custom script failed
""" """
super(CustomBackupMethod, self).mount(restore_manager) super(CustomBackupMethod, self).mount()
ret = hook_callback('backup_method', [self.method], ret = hook_callback('backup_method', [self.method],
args=self._get_args('mount')) args=self._get_args('mount'))
@ -2160,9 +2165,9 @@ def backup_create(name=None, description=None, methods=[],
# Add backup methods # Add backup methods
if output_directory: if output_directory:
methods = BackupMethod.create(methods, output_directory) methods = BackupMethod.create(methods, backup_manager, output_directory)
else: else:
methods = BackupMethod.create(methods) methods = BackupMethod.create(methods, backup_manager)
for method in methods: for method in methods:
backup_manager.add(method) backup_manager.add(method)