mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[fix] Pep8 and syntax
This commit is contained in:
parent
72daac7244
commit
218865a59e
1 changed files with 123 additions and 34 deletions
157
src/backup.py
157
src/backup.py
|
@ -26,22 +26,19 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import tarfile
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import csv
|
import csv
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import re
|
||||||
|
import urllib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from glob import glob
|
|
||||||
from collections import OrderedDict
|
|
||||||
from functools import reduce
|
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
from moulinette import Moulinette, m18n
|
from moulinette import Moulinette, m18n
|
||||||
from moulinette.utils import filesystem
|
from moulinette.utils import filesystem
|
||||||
from moulinette.core import MoulinetteError
|
|
||||||
from moulinette.utils.log import getActionLogger
|
from moulinette.utils.log import getActionLogger
|
||||||
from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml
|
from moulinette.utils.filesystem import mkdir, write_to_yaml, read_yaml, write_to_file
|
||||||
from moulinette.utils.process import check_output
|
from moulinette.utils.process import check_output
|
||||||
|
|
||||||
import yunohost.domain
|
import yunohost.domain
|
||||||
|
@ -66,11 +63,11 @@ from yunohost.tools import (
|
||||||
)
|
)
|
||||||
from yunohost.regenconf import regen_conf
|
from yunohost.regenconf import regen_conf
|
||||||
from yunohost.log import OperationLogger, is_unit_operation
|
from yunohost.log import OperationLogger, is_unit_operation
|
||||||
from yunohost.repository import BackupRepository
|
from yunohost.repository import BackupRepository, BackupArchive
|
||||||
|
from yunohost.config import ConfigPanel
|
||||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||||
from yunohost.utils.packages import ynh_packages_version
|
from yunohost.utils.packages import ynh_packages_version
|
||||||
from yunohost.utils.filesystem import free_space_in_directory, disk_usage, binary_to_human
|
from yunohost.utils.filesystem import free_space_in_directory, disk_usage, binary_to_human
|
||||||
from yunohost.settings import settings_get
|
|
||||||
|
|
||||||
BACKUP_PATH = "/home/yunohost.backup"
|
BACKUP_PATH = "/home/yunohost.backup"
|
||||||
ARCHIVES_PATH = f"{BACKUP_PATH}/archives"
|
ARCHIVES_PATH = f"{BACKUP_PATH}/archives"
|
||||||
|
@ -113,6 +110,7 @@ class BackupRestoreTargetsManager:
|
||||||
self.results[category][element] = value
|
self.results[category][element] = value
|
||||||
else:
|
else:
|
||||||
currentValue = self.results[category][element]
|
currentValue = self.results[category][element]
|
||||||
|
|
||||||
if levels.index(currentValue) > levels.index(value):
|
if levels.index(currentValue) > levels.index(value):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -146,6 +144,7 @@ class BackupRestoreTargetsManager:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If no targets wanted, set as empty list
|
# If no targets wanted, set as empty list
|
||||||
|
|
||||||
if wanted_targets is None:
|
if wanted_targets is None:
|
||||||
self.targets[category] = []
|
self.targets[category] = []
|
||||||
|
|
||||||
|
@ -171,6 +170,7 @@ class BackupRestoreTargetsManager:
|
||||||
error_if_wanted_target_is_unavailable(target)
|
error_if_wanted_target_is_unavailable(target)
|
||||||
|
|
||||||
# For target with no result yet (like 'Skipped'), set it as unknown
|
# For target with no result yet (like 'Skipped'), set it as unknown
|
||||||
|
|
||||||
if self.targets[category] is not None:
|
if self.targets[category] is not None:
|
||||||
for target in self.targets[category]:
|
for target in self.targets[category]:
|
||||||
self.set_result(category, target, "Unknown")
|
self.set_result(category, target, "Unknown")
|
||||||
|
@ -192,14 +192,18 @@ class BackupRestoreTargetsManager:
|
||||||
if include:
|
if include:
|
||||||
return [
|
return [
|
||||||
target
|
target
|
||||||
|
|
||||||
for target in self.targets[category]
|
for target in self.targets[category]
|
||||||
|
|
||||||
if self.results[category][target] in include
|
if self.results[category][target] in include
|
||||||
]
|
]
|
||||||
|
|
||||||
if exclude:
|
if exclude:
|
||||||
return [
|
return [
|
||||||
target
|
target
|
||||||
|
|
||||||
for target in self.targets[category]
|
for target in self.targets[category]
|
||||||
|
|
||||||
if self.results[category][target] not in exclude
|
if self.results[category][target] not in exclude
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -284,17 +288,18 @@ class BackupManager:
|
||||||
self.targets = BackupRestoreTargetsManager()
|
self.targets = BackupRestoreTargetsManager()
|
||||||
|
|
||||||
# Define backup name if needed
|
# Define backup name if needed
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
name = self._define_backup_name()
|
name = self._define_backup_name()
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
# Define working directory if needed and initialize it
|
# Define working directory if needed and initialize it
|
||||||
self.work_dir = work_dir
|
self.work_dir = work_dir
|
||||||
|
|
||||||
if self.work_dir is None:
|
if self.work_dir is None:
|
||||||
self.work_dir = os.path.join(BACKUP_PATH, "tmp", name)
|
self.work_dir = os.path.join(BACKUP_PATH, "tmp", name)
|
||||||
self._init_work_dir()
|
self._init_work_dir()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Misc helpers
|
# Misc helpers
|
||||||
#
|
#
|
||||||
|
@ -302,6 +307,7 @@ class BackupManager:
|
||||||
@property
|
@property
|
||||||
def info(self):
|
def info(self):
|
||||||
"""(Getter) Dict containing info about the archive being created"""
|
"""(Getter) Dict containing info about the archive being created"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"description": self.description,
|
"description": self.description,
|
||||||
"created_at": self.created_at,
|
"created_at": self.created_at,
|
||||||
|
@ -316,6 +322,7 @@ class BackupManager:
|
||||||
def is_tmp_work_dir(self):
|
def is_tmp_work_dir(self):
|
||||||
"""(Getter) Return true if the working directory is temporary and should
|
"""(Getter) Return true if the working directory is temporary and should
|
||||||
be clean at the end of the backup"""
|
be clean at the end of the backup"""
|
||||||
|
|
||||||
return self.work_dir == os.path.join(BACKUP_PATH, "tmp", self.name)
|
return self.work_dir == os.path.join(BACKUP_PATH, "tmp", self.name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -328,6 +335,7 @@ class BackupManager:
|
||||||
(string) A backup name created from current date 'YYMMDD-HHMMSS'
|
(string) A backup name created from current date 'YYMMDD-HHMMSS'
|
||||||
"""
|
"""
|
||||||
# FIXME: case where this name already exist
|
# FIXME: case where this name already exist
|
||||||
|
|
||||||
return time.strftime("%Y%m%d-%H%M%S", time.gmtime())
|
return time.strftime("%Y%m%d-%H%M%S", time.gmtime())
|
||||||
|
|
||||||
def _init_work_dir(self):
|
def _init_work_dir(self):
|
||||||
|
@ -338,6 +346,7 @@ class BackupManager:
|
||||||
|
|
||||||
# FIXME replace isdir by exists ? manage better the case where the path
|
# FIXME replace isdir by exists ? manage better the case where the path
|
||||||
# exists
|
# exists
|
||||||
|
|
||||||
if not os.path.isdir(self.work_dir):
|
if not os.path.isdir(self.work_dir):
|
||||||
filesystem.mkdir(self.work_dir, 0o750, parents=True, uid="admin")
|
filesystem.mkdir(self.work_dir, 0o750, parents=True, uid="admin")
|
||||||
elif self.is_tmp_work_dir:
|
elif self.is_tmp_work_dir:
|
||||||
|
@ -348,6 +357,7 @@ class BackupManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Try to recursively unmount stuff (from a previously failed backup ?)
|
# Try to recursively unmount stuff (from a previously failed backup ?)
|
||||||
|
|
||||||
if not _recursive_umount(self.work_dir):
|
if not _recursive_umount(self.work_dir):
|
||||||
raise YunohostValidationError("backup_output_directory_not_empty")
|
raise YunohostValidationError("backup_output_directory_not_empty")
|
||||||
else:
|
else:
|
||||||
|
@ -448,9 +458,11 @@ class BackupManager:
|
||||||
# => "wordpress" dir will be put inside "sources/" and won't be renamed
|
# => "wordpress" dir will be put inside "sources/" and won't be renamed
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if dest is None:
|
if dest is None:
|
||||||
dest = source
|
dest = source
|
||||||
source = os.path.join(self.work_dir, source)
|
source = os.path.join(self.work_dir, source)
|
||||||
|
|
||||||
if dest.endswith("/"):
|
if dest.endswith("/"):
|
||||||
dest = os.path.join(dest, os.path.basename(source))
|
dest = os.path.join(dest, os.path.basename(source))
|
||||||
self.paths_to_backup.append({"source": source, "dest": dest})
|
self.paths_to_backup.append({"source": source, "dest": dest})
|
||||||
|
@ -538,10 +550,13 @@ class BackupManager:
|
||||||
# Add unlisted files from backup tmp dir
|
# Add unlisted files from backup tmp dir
|
||||||
self._add_to_list_to_backup("backup.csv")
|
self._add_to_list_to_backup("backup.csv")
|
||||||
self._add_to_list_to_backup("info.json")
|
self._add_to_list_to_backup("info.json")
|
||||||
|
|
||||||
for app in self.apps_return.keys():
|
for app in self.apps_return.keys():
|
||||||
self._add_to_list_to_backup(f"apps/{app}")
|
self._add_to_list_to_backup(f"apps/{app}")
|
||||||
|
|
||||||
if os.path.isdir(os.path.join(self.work_dir, "conf")):
|
if os.path.isdir(os.path.join(self.work_dir, "conf")):
|
||||||
self._add_to_list_to_backup("conf")
|
self._add_to_list_to_backup("conf")
|
||||||
|
|
||||||
if os.path.isdir(os.path.join(self.work_dir, "data")):
|
if os.path.isdir(os.path.join(self.work_dir, "data")):
|
||||||
self._add_to_list_to_backup("data")
|
self._add_to_list_to_backup("data")
|
||||||
|
|
||||||
|
@ -599,6 +614,7 @@ class BackupManager:
|
||||||
system_targets = self.targets.list("system", exclude=["Skipped"])
|
system_targets = self.targets.list("system", exclude=["Skipped"])
|
||||||
|
|
||||||
# If nothing to backup, return immediately
|
# If nothing to backup, return immediately
|
||||||
|
|
||||||
if system_targets == []:
|
if system_targets == []:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -621,14 +637,18 @@ class BackupManager:
|
||||||
hook: [
|
hook: [
|
||||||
path for path, result in infos.items() if result["state"] == "succeed"
|
path for path, result in infos.items() if result["state"] == "succeed"
|
||||||
]
|
]
|
||||||
|
|
||||||
for hook, infos in ret.items()
|
for hook, infos in ret.items()
|
||||||
|
|
||||||
if any(result["state"] == "succeed" for result in infos.values())
|
if any(result["state"] == "succeed" for result in infos.values())
|
||||||
}
|
}
|
||||||
ret_failed = {
|
ret_failed = {
|
||||||
hook: [
|
hook: [
|
||||||
path for path, result in infos.items() if result["state"] == "failed"
|
path for path, result in infos.items() if result["state"] == "failed"
|
||||||
]
|
]
|
||||||
|
|
||||||
for hook, infos in ret.items()
|
for hook, infos in ret.items()
|
||||||
|
|
||||||
if any(result["state"] == "failed" for result in infos.values())
|
if any(result["state"] == "failed" for result in infos.values())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,6 +663,7 @@ class BackupManager:
|
||||||
# a restore hook available)
|
# a restore hook available)
|
||||||
|
|
||||||
restore_hooks_dir = os.path.join(self.work_dir, "hooks", "restore")
|
restore_hooks_dir = os.path.join(self.work_dir, "hooks", "restore")
|
||||||
|
|
||||||
if not os.path.exists(restore_hooks_dir):
|
if not os.path.exists(restore_hooks_dir):
|
||||||
filesystem.mkdir(restore_hooks_dir, mode=0o700, parents=True, uid="root")
|
filesystem.mkdir(restore_hooks_dir, mode=0o700, parents=True, uid="root")
|
||||||
|
|
||||||
|
@ -651,6 +672,7 @@ class BackupManager:
|
||||||
for part in ret_succeed.keys():
|
for part in ret_succeed.keys():
|
||||||
if part in restore_hooks:
|
if part in restore_hooks:
|
||||||
part_restore_hooks = hook_info("restore", part)["hooks"]
|
part_restore_hooks = hook_info("restore", part)["hooks"]
|
||||||
|
|
||||||
for hook in part_restore_hooks:
|
for hook in part_restore_hooks:
|
||||||
self._add_to_list_to_backup(hook["path"], "hooks/restore/")
|
self._add_to_list_to_backup(hook["path"], "hooks/restore/")
|
||||||
self.targets.set_result("system", part, "Success")
|
self.targets.set_result("system", part, "Success")
|
||||||
|
@ -785,8 +807,10 @@ class BackupManager:
|
||||||
# FIXME Some archive will set up dependencies, those are not in this
|
# FIXME Some archive will set up dependencies, those are not in this
|
||||||
# size info
|
# size info
|
||||||
self.size = 0
|
self.size = 0
|
||||||
|
|
||||||
for system_key in self.system_return:
|
for system_key in self.system_return:
|
||||||
self.size_details["system"][system_key] = 0
|
self.size_details["system"][system_key] = 0
|
||||||
|
|
||||||
for app_key in self.apps_return:
|
for app_key in self.apps_return:
|
||||||
self.size_details["apps"][app_key] = 0
|
self.size_details["apps"][app_key] = 0
|
||||||
|
|
||||||
|
@ -799,10 +823,12 @@ class BackupManager:
|
||||||
# Add size to apps details
|
# Add size to apps details
|
||||||
splitted_dest = row["dest"].split("/")
|
splitted_dest = row["dest"].split("/")
|
||||||
category = splitted_dest[0]
|
category = splitted_dest[0]
|
||||||
|
|
||||||
if category == "apps":
|
if category == "apps":
|
||||||
for app_key in self.apps_return:
|
for app_key in self.apps_return:
|
||||||
if row["dest"].startswith("apps/" + app_key):
|
if row["dest"].startswith("apps/" + app_key):
|
||||||
self.size_details["apps"][app_key] += size
|
self.size_details["apps"][app_key] += size
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# OR Add size to the correct system element
|
# OR Add size to the correct system element
|
||||||
|
@ -810,6 +836,7 @@ class BackupManager:
|
||||||
for system_key in self.system_return:
|
for system_key in self.system_return:
|
||||||
if row["dest"].startswith(system_key.replace("_", "/")):
|
if row["dest"].startswith(system_key.replace("_", "/")):
|
||||||
self.size_details["system"][system_key] += size
|
self.size_details["system"][system_key] += size
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
self.size += size
|
self.size += size
|
||||||
|
@ -897,6 +924,7 @@ class RestoreManager:
|
||||||
self.info = json.load(f)
|
self.info = json.load(f)
|
||||||
|
|
||||||
# Historically, "system" was "hooks"
|
# Historically, "system" was "hooks"
|
||||||
|
|
||||||
if "system" not in self.info.keys():
|
if "system" not in self.info.keys():
|
||||||
self.info["system"] = self.info["hooks"]
|
self.info["system"] = self.info["hooks"]
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -916,6 +944,7 @@ class RestoreManager:
|
||||||
Post install yunohost if needed
|
Post install yunohost if needed
|
||||||
"""
|
"""
|
||||||
# Check if YunoHost is installed
|
# Check if YunoHost is installed
|
||||||
|
|
||||||
if not os.path.isfile("/etc/yunohost/installed"):
|
if not os.path.isfile("/etc/yunohost/installed"):
|
||||||
# Retrieve the domain from the backup
|
# Retrieve the domain from the backup
|
||||||
try:
|
try:
|
||||||
|
@ -945,6 +974,7 @@ class RestoreManager:
|
||||||
|
|
||||||
if os.path.ismount(self.work_dir):
|
if os.path.ismount(self.work_dir):
|
||||||
ret = subprocess.call(["umount", self.work_dir])
|
ret = subprocess.call(["umount", self.work_dir])
|
||||||
|
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
logger.warning(m18n.n("restore_cleaning_failed"))
|
logger.warning(m18n.n("restore_cleaning_failed"))
|
||||||
filesystem.rm(self.work_dir, recursive=True, force=True)
|
filesystem.rm(self.work_dir, recursive=True, force=True)
|
||||||
|
@ -992,6 +1022,7 @@ class RestoreManager:
|
||||||
# Otherwise, attempt to find it (or them?) in the archive
|
# Otherwise, attempt to find it (or them?) in the archive
|
||||||
|
|
||||||
# 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 (
|
if (
|
||||||
system_part not in self.info["system"]
|
system_part not in self.info["system"]
|
||||||
or "paths" not in self.info["system"][system_part]
|
or "paths" not in self.info["system"][system_part]
|
||||||
|
@ -999,6 +1030,7 @@ class RestoreManager:
|
||||||
):
|
):
|
||||||
logger.error(m18n.n("restore_hook_unavailable", part=system_part))
|
logger.error(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 = self.info["system"][system_part]["paths"]
|
||||||
|
@ -1006,6 +1038,7 @@ class RestoreManager:
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
for hook_path in hook_paths:
|
for hook_path in hook_paths:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Adding restoration script '%s' to the system "
|
"Adding restoration script '%s' to the system "
|
||||||
|
@ -1036,6 +1069,7 @@ class RestoreManager:
|
||||||
# Otherwise, if at least one app can be restored, we keep going on
|
# Otherwise, if at least one app can be restored, we keep going on
|
||||||
# because those which can be restored will indeed be restored
|
# because those which can be restored will indeed be restored
|
||||||
already_installed = [app for app in to_be_restored if _is_installed(app)]
|
already_installed = [app for app in to_be_restored if _is_installed(app)]
|
||||||
|
|
||||||
if already_installed != []:
|
if already_installed != []:
|
||||||
if already_installed == to_be_restored:
|
if already_installed == to_be_restored:
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
|
@ -1067,6 +1101,7 @@ class RestoreManager:
|
||||||
if os.path.ismount(self.work_dir):
|
if os.path.ismount(self.work_dir):
|
||||||
logger.debug("An already mounting point '%s' already exists", self.work_dir)
|
logger.debug("An already mounting point '%s' already exists", self.work_dir)
|
||||||
ret = subprocess.call(["umount", self.work_dir])
|
ret = subprocess.call(["umount", self.work_dir])
|
||||||
|
|
||||||
if ret == 0:
|
if ret == 0:
|
||||||
subprocess.call(["rmdir", self.work_dir])
|
subprocess.call(["rmdir", self.work_dir])
|
||||||
logger.debug(f"Unmount dir: {self.work_dir}")
|
logger.debug(f"Unmount dir: {self.work_dir}")
|
||||||
|
@ -1077,6 +1112,7 @@ class RestoreManager:
|
||||||
"temporary restore directory '%s' already exists", self.work_dir
|
"temporary restore directory '%s' already exists", self.work_dir
|
||||||
)
|
)
|
||||||
ret = subprocess.call(["rm", "-Rf", self.work_dir])
|
ret = subprocess.call(["rm", "-Rf", self.work_dir])
|
||||||
|
|
||||||
if ret == 0:
|
if ret == 0:
|
||||||
logger.debug(f"Delete dir: {self.work_dir}")
|
logger.debug(f"Delete dir: {self.work_dir}")
|
||||||
else:
|
else:
|
||||||
|
@ -1108,8 +1144,10 @@ class RestoreManager:
|
||||||
|
|
||||||
# If complete restore operations (or legacy archive)
|
# If complete restore operations (or legacy archive)
|
||||||
margin = CONF_MARGIN_SPACE_SIZE * 1024 * 1024
|
margin = CONF_MARGIN_SPACE_SIZE * 1024 * 1024
|
||||||
|
|
||||||
if (restore_all_system and restore_all_apps) or "size_details" not in self.info:
|
if (restore_all_system and restore_all_apps) or "size_details" not in self.info:
|
||||||
size = self.info["size"]
|
size = self.info["size"]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
"size_details" not in self.info
|
"size_details" not in self.info
|
||||||
or self.info["size_details"]["apps"] != {}
|
or self.info["size_details"]["apps"] != {}
|
||||||
|
@ -1118,11 +1156,13 @@ class RestoreManager:
|
||||||
# Partial restore don't need all backup size
|
# Partial restore don't need all backup size
|
||||||
else:
|
else:
|
||||||
size = 0
|
size = 0
|
||||||
|
|
||||||
if system is not None:
|
if system is not None:
|
||||||
for system_element in system:
|
for system_element in system:
|
||||||
size += self.info["size_details"]["system"][system_element]
|
size += self.info["size_details"]["system"][system_element]
|
||||||
|
|
||||||
# TODO how to know the dependencies size ?
|
# TODO how to know the dependencies size ?
|
||||||
|
|
||||||
if apps is not None:
|
if apps is not None:
|
||||||
for app in apps:
|
for app in apps:
|
||||||
size += self.info["size_details"]["apps"][app]
|
size += self.info["size_details"]["apps"][app]
|
||||||
|
@ -1130,6 +1170,7 @@ class RestoreManager:
|
||||||
|
|
||||||
if not os.path.isfile("/etc/yunohost/installed"):
|
if not os.path.isfile("/etc/yunohost/installed"):
|
||||||
size += POSTINSTALL_ESTIMATE_SPACE_SIZE * 1024 * 1024
|
size += POSTINSTALL_ESTIMATE_SPACE_SIZE * 1024 * 1024
|
||||||
|
|
||||||
return (size, margin)
|
return (size, margin)
|
||||||
|
|
||||||
def assert_enough_free_space(self):
|
def assert_enough_free_space(self):
|
||||||
|
@ -1140,6 +1181,7 @@ class RestoreManager:
|
||||||
free_space = free_space_in_directory(BACKUP_PATH)
|
free_space = free_space_in_directory(BACKUP_PATH)
|
||||||
|
|
||||||
(needed_space, margin) = self._compute_needed_space()
|
(needed_space, margin) = self._compute_needed_space()
|
||||||
|
|
||||||
if free_space >= needed_space + margin:
|
if free_space >= needed_space + margin:
|
||||||
return True
|
return True
|
||||||
elif free_space > needed_space:
|
elif free_space > needed_space:
|
||||||
|
@ -1200,6 +1242,7 @@ class RestoreManager:
|
||||||
with open(backup_csv) as csvfile:
|
with open(backup_csv) as csvfile:
|
||||||
reader = csv.DictReader(csvfile, fieldnames=["source", "dest"])
|
reader = csv.DictReader(csvfile, fieldnames=["source", "dest"])
|
||||||
newlines = []
|
newlines = []
|
||||||
|
|
||||||
for row in reader:
|
for row in reader:
|
||||||
for pattern, replace in LEGACY_PHP_VERSION_REPLACEMENTS:
|
for pattern, replace in LEGACY_PHP_VERSION_REPLACEMENTS:
|
||||||
if pattern in row["source"]:
|
if pattern in row["source"]:
|
||||||
|
@ -1215,6 +1258,7 @@ class RestoreManager:
|
||||||
writer = csv.DictWriter(
|
writer = csv.DictWriter(
|
||||||
csvfile, fieldnames=["source", "dest"], quoting=csv.QUOTE_ALL
|
csvfile, fieldnames=["source", "dest"], quoting=csv.QUOTE_ALL
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in newlines:
|
for row in newlines:
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
|
@ -1224,6 +1268,7 @@ class RestoreManager:
|
||||||
system_targets = self.targets.list("system", exclude=["Skipped"])
|
system_targets = self.targets.list("system", exclude=["Skipped"])
|
||||||
|
|
||||||
# If nothing to restore, return immediately
|
# If nothing to restore, return immediately
|
||||||
|
|
||||||
if system_targets == []:
|
if system_targets == []:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1262,12 +1307,16 @@ class RestoreManager:
|
||||||
|
|
||||||
ret_succeed = [
|
ret_succeed = [
|
||||||
hook
|
hook
|
||||||
|
|
||||||
for hook, infos in ret.items()
|
for hook, infos in ret.items()
|
||||||
|
|
||||||
if any(result["state"] == "succeed" for result in infos.values())
|
if any(result["state"] == "succeed" for result in infos.values())
|
||||||
]
|
]
|
||||||
ret_failed = [
|
ret_failed = [
|
||||||
hook
|
hook
|
||||||
|
|
||||||
for hook, infos in ret.items()
|
for hook, infos in ret.items()
|
||||||
|
|
||||||
if any(result["state"] == "failed" for result in infos.values())
|
if any(result["state"] == "failed" for result in infos.values())
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1275,6 +1324,7 @@ class RestoreManager:
|
||||||
self.targets.set_result("system", part, "Success")
|
self.targets.set_result("system", part, "Success")
|
||||||
|
|
||||||
error_part = []
|
error_part = []
|
||||||
|
|
||||||
for part in ret_failed:
|
for part in ret_failed:
|
||||||
logger.error(m18n.n("restore_system_part_failed", part=part))
|
logger.error(m18n.n("restore_system_part_failed", part=part))
|
||||||
self.targets.set_result("system", part, "Error")
|
self.targets.set_result("system", part, "Error")
|
||||||
|
@ -1296,14 +1346,17 @@ class RestoreManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove all permission for all app still in the LDAP
|
# Remove all permission for all app still in the LDAP
|
||||||
|
|
||||||
for permission_name in user_permission_list(ignore_system_perms=True)[
|
for permission_name in user_permission_list(ignore_system_perms=True)[
|
||||||
"permissions"
|
"permissions"
|
||||||
].keys():
|
].keys():
|
||||||
permission_delete(permission_name, force=True, sync_perm=False)
|
permission_delete(permission_name, force=True, sync_perm=False)
|
||||||
|
|
||||||
# Restore permission for apps installed
|
# Restore permission for apps installed
|
||||||
|
|
||||||
for permission_name, permission_infos in old_apps_permission.items():
|
for permission_name, permission_infos in old_apps_permission.items():
|
||||||
app_name, perm_name = permission_name.split(".")
|
app_name, perm_name = permission_name.split(".")
|
||||||
|
|
||||||
if _is_installed(app_name):
|
if _is_installed(app_name):
|
||||||
permission_create(
|
permission_create(
|
||||||
permission_name,
|
permission_name,
|
||||||
|
@ -1312,6 +1365,7 @@ class RestoreManager:
|
||||||
additional_urls=permission_infos["additional_urls"],
|
additional_urls=permission_infos["additional_urls"],
|
||||||
auth_header=permission_infos["auth_header"],
|
auth_header=permission_infos["auth_header"],
|
||||||
label=permission_infos["label"]
|
label=permission_infos["label"]
|
||||||
|
|
||||||
if perm_name == "main"
|
if perm_name == "main"
|
||||||
else permission_infos["sublabel"],
|
else permission_infos["sublabel"],
|
||||||
show_tile=permission_infos["show_tile"],
|
show_tile=permission_infos["show_tile"],
|
||||||
|
@ -1368,15 +1422,18 @@ class RestoreManager:
|
||||||
for item in os.listdir(src):
|
for item in os.listdir(src):
|
||||||
s = os.path.join(src, item)
|
s = os.path.join(src, item)
|
||||||
d = os.path.join(dst, item)
|
d = os.path.join(dst, item)
|
||||||
|
|
||||||
if os.path.isdir(s):
|
if os.path.isdir(s):
|
||||||
shutil.copytree(s, d, symlinks, ignore)
|
shutil.copytree(s, d, symlinks, ignore)
|
||||||
else:
|
else:
|
||||||
shutil.copy2(s, d)
|
shutil.copy2(s, d)
|
||||||
|
|
||||||
# Check if the app is not already installed
|
# Check if the app is not already installed
|
||||||
|
|
||||||
if _is_installed(app_instance_name):
|
if _is_installed(app_instance_name):
|
||||||
logger.error(m18n.n("restore_already_installed_app", app=app_instance_name))
|
logger.error(m18n.n("restore_already_installed_app", app=app_instance_name))
|
||||||
self.targets.set_result("apps", app_instance_name, "Error")
|
self.targets.set_result("apps", app_instance_name, "Error")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Start register change on system
|
# Start register change on system
|
||||||
|
@ -1404,9 +1461,11 @@ class RestoreManager:
|
||||||
|
|
||||||
# Check if the app has a restore script
|
# Check if the app has a restore script
|
||||||
app_restore_script_in_archive = os.path.join(app_scripts_in_archive, "restore")
|
app_restore_script_in_archive = os.path.join(app_scripts_in_archive, "restore")
|
||||||
|
|
||||||
if not os.path.isfile(app_restore_script_in_archive):
|
if not os.path.isfile(app_restore_script_in_archive):
|
||||||
logger.warning(m18n.n("unrestore_app", app=app_instance_name))
|
logger.warning(m18n.n("unrestore_app", app=app_instance_name))
|
||||||
self.targets.set_result("apps", app_instance_name, "Warning")
|
self.targets.set_result("apps", app_instance_name, "Warning")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1427,6 +1486,7 @@ class RestoreManager:
|
||||||
restore_script = os.path.join(tmp_workdir_for_app, "restore")
|
restore_script = os.path.join(tmp_workdir_for_app, "restore")
|
||||||
|
|
||||||
# Restore permissions
|
# Restore permissions
|
||||||
|
|
||||||
if not os.path.isfile(f"{app_settings_new_path}/permissions.yml"):
|
if not os.path.isfile(f"{app_settings_new_path}/permissions.yml"):
|
||||||
raise YunohostError(
|
raise YunohostError(
|
||||||
"Didnt find a permssions.yml for the app !?", raw_msg=True
|
"Didnt find a permssions.yml for the app !?", raw_msg=True
|
||||||
|
@ -1455,6 +1515,7 @@ class RestoreManager:
|
||||||
additional_urls=permission_infos.get("additional_urls"),
|
additional_urls=permission_infos.get("additional_urls"),
|
||||||
auth_header=permission_infos.get("auth_header"),
|
auth_header=permission_infos.get("auth_header"),
|
||||||
label=permission_infos.get("label")
|
label=permission_infos.get("label")
|
||||||
|
|
||||||
if perm_name == "main"
|
if perm_name == "main"
|
||||||
else permission_infos.get("sublabel"),
|
else permission_infos.get("sublabel"),
|
||||||
show_tile=permission_infos.get("show_tile", True),
|
show_tile=permission_infos.get("show_tile", True),
|
||||||
|
@ -1548,6 +1609,7 @@ class RestoreManager:
|
||||||
remove_operation_logger.start()
|
remove_operation_logger.start()
|
||||||
|
|
||||||
# Execute remove script
|
# Execute remove script
|
||||||
|
|
||||||
if hook_exec(remove_script, env=env_dict_remove)[0] != 0:
|
if hook_exec(remove_script, env=env_dict_remove)[0] != 0:
|
||||||
msg = m18n.n("app_not_properly_removed", app=app_instance_name)
|
msg = m18n.n("app_not_properly_removed", app=app_instance_name)
|
||||||
logger.warning(msg)
|
logger.warning(msg)
|
||||||
|
@ -1559,6 +1621,7 @@ class RestoreManager:
|
||||||
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
||||||
|
|
||||||
# Remove all permission in LDAP for this app
|
# Remove all permission in LDAP for this app
|
||||||
|
|
||||||
for permission_name in user_permission_list()["permissions"].keys():
|
for permission_name in user_permission_list()["permissions"].keys():
|
||||||
if permission_name.startswith(app_instance_name + "."):
|
if permission_name.startswith(app_instance_name + "."):
|
||||||
permission_delete(permission_name, force=True)
|
permission_delete(permission_name, force=True)
|
||||||
|
@ -1577,7 +1640,7 @@ def backup_create(
|
||||||
operation_logger,
|
operation_logger,
|
||||||
name=None,
|
name=None,
|
||||||
description=None,
|
description=None,
|
||||||
reposistories=[],
|
repositories=[],
|
||||||
system=[],
|
system=[],
|
||||||
apps=[],
|
apps=[],
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
|
@ -1588,7 +1651,7 @@ def backup_create(
|
||||||
Keyword arguments:
|
Keyword arguments:
|
||||||
name -- Name of the backup archive
|
name -- Name of the backup archive
|
||||||
description -- Short description of the backup
|
description -- Short description of the backup
|
||||||
method -- Method of backup to use
|
repositories -- Repositories in which we want to save the backup
|
||||||
output_directory -- Output directory for the backup
|
output_directory -- Output directory for the backup
|
||||||
system -- List of system elements to backup
|
system -- List of system elements to backup
|
||||||
apps -- List of application names to backup
|
apps -- List of application names to backup
|
||||||
|
@ -1601,10 +1664,12 @@ def backup_create(
|
||||||
#
|
#
|
||||||
|
|
||||||
# Validate there is no archive with the same name
|
# Validate there is no archive with the same name
|
||||||
|
|
||||||
if name and name in backup_list(repositories)["archives"]:
|
if name and name in backup_list(repositories)["archives"]:
|
||||||
raise YunohostValidationError("backup_archive_name_exists")
|
raise YunohostValidationError("backup_archive_name_exists")
|
||||||
|
|
||||||
# If no --system or --apps given, backup everything
|
# If no --system or --apps given, backup everything
|
||||||
|
|
||||||
if system is None and apps is None:
|
if system is None and apps is None:
|
||||||
system = []
|
system = []
|
||||||
apps = []
|
apps = []
|
||||||
|
@ -1616,13 +1681,14 @@ def backup_create(
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
# Create yunohost archives directory if it does not exists
|
# Create yunohost archives directory if it does not exists
|
||||||
_create_archive_dir() # FIXME
|
_create_archive_dir() # FIXME
|
||||||
|
|
||||||
|
# Add backup repositories
|
||||||
|
|
||||||
# Add backup methods
|
|
||||||
if not repositories:
|
if not repositories:
|
||||||
repositories = ["local-borg"]
|
repositories = ["local-borg"]
|
||||||
|
|
||||||
repositories = [BackupRepository(repo) for repo in reposistories]
|
repositories = [BackupRepository(repo) for repo in repositories]
|
||||||
|
|
||||||
# Prepare files to backup
|
# Prepare files to backup
|
||||||
backup_manager = BackupManager(name, description,
|
backup_manager = BackupManager(name, description,
|
||||||
|
@ -1686,6 +1752,7 @@ def backup_restore(name, system=[], apps=[], force=False):
|
||||||
#
|
#
|
||||||
|
|
||||||
# If no --system or --apps given, restore everything
|
# If no --system or --apps given, restore everything
|
||||||
|
|
||||||
if system is None and apps is None:
|
if system is None and apps is None:
|
||||||
system = []
|
system = []
|
||||||
apps = []
|
apps = []
|
||||||
|
@ -1714,6 +1781,7 @@ def backup_restore(name, system=[], apps=[], force=False):
|
||||||
"/etc/yunohost/installed"
|
"/etc/yunohost/installed"
|
||||||
):
|
):
|
||||||
logger.warning(m18n.n("yunohost_already_installed"))
|
logger.warning(m18n.n("yunohost_already_installed"))
|
||||||
|
|
||||||
if not force:
|
if not force:
|
||||||
try:
|
try:
|
||||||
# Ask confirmation for restoring
|
# Ask confirmation for restoring
|
||||||
|
@ -1725,6 +1793,7 @@ def backup_restore(name, system=[], apps=[], force=False):
|
||||||
else:
|
else:
|
||||||
if i == "y" or i == "Y":
|
if i == "y" or i == "Y":
|
||||||
force = True
|
force = True
|
||||||
|
|
||||||
if not force:
|
if not force:
|
||||||
raise YunohostError("restore_failed")
|
raise YunohostError("restore_failed")
|
||||||
|
|
||||||
|
@ -1737,6 +1806,7 @@ def backup_restore(name, system=[], apps=[], force=False):
|
||||||
restore_manager.restore()
|
restore_manager.restore()
|
||||||
|
|
||||||
# Check if something has been restored
|
# Check if something has been restored
|
||||||
|
|
||||||
if restore_manager.success:
|
if restore_manager.success:
|
||||||
logger.success(m18n.n("restore_complete"))
|
logger.success(m18n.n("restore_complete"))
|
||||||
else:
|
else:
|
||||||
|
@ -1755,23 +1825,30 @@ def backup_list(repositories=[], with_info=False, human_readable=False):
|
||||||
human_readable -- Print sizes in human readable format
|
human_readable -- Print sizes in human readable format
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: BackupRepository(name).list(with_info)
|
name: BackupRepository(name).list(with_info)
|
||||||
|
|
||||||
for name in repositories or BackupRepository.list(full=False)
|
for name in repositories or BackupRepository.list(full=False)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def backup_download(name, repository):
|
def backup_download(name, repository):
|
||||||
|
|
||||||
repo = BackupRepository(repo)
|
repo = BackupRepository(repository)
|
||||||
archive = BackupArchive(name, repo)
|
archive = BackupArchive(name, repo)
|
||||||
|
|
||||||
return archive.download()
|
return archive.download()
|
||||||
|
|
||||||
|
|
||||||
def backup_mount(name, repository, path):
|
def backup_mount(name, repository, path):
|
||||||
|
|
||||||
repo = BackupRepository(repo)
|
repo = BackupRepository(repository)
|
||||||
archive = BackupArchive(name, repo)
|
archive = BackupArchive(name, repo)
|
||||||
|
|
||||||
return archive.mount(path)
|
return archive.mount(path)
|
||||||
|
|
||||||
|
|
||||||
def backup_info(name, repository=None, with_details=False, human_readable=False):
|
def backup_info(name, repository=None, with_details=False, human_readable=False):
|
||||||
"""
|
"""
|
||||||
Get info about a local backup archive
|
Get info about a local backup archive
|
||||||
|
@ -1782,10 +1859,12 @@ def backup_info(name, repository=None, with_details=False, human_readable=False)
|
||||||
human_readable -- Print sizes in human readable format
|
human_readable -- Print sizes in human readable format
|
||||||
|
|
||||||
"""
|
"""
|
||||||
repo = BackupRepository(repo)
|
repo = BackupRepository(repository)
|
||||||
archive = BackupArchive(name, repo)
|
archive = BackupArchive(name, repo)
|
||||||
|
|
||||||
return archive.info()
|
return archive.info()
|
||||||
|
|
||||||
|
|
||||||
def backup_delete(name, repository):
|
def backup_delete(name, repository):
|
||||||
"""
|
"""
|
||||||
Delete a backup
|
Delete a backup
|
||||||
|
@ -1794,7 +1873,7 @@ def backup_delete(name, repository):
|
||||||
name -- Name of the local backup archive
|
name -- Name of the local backup archive
|
||||||
|
|
||||||
"""
|
"""
|
||||||
repo = BackupRepository(repo)
|
repo = BackupRepository(repository)
|
||||||
archive = BackupArchive(name, repo)
|
archive = BackupArchive(name, repo)
|
||||||
|
|
||||||
# FIXME Those are really usefull ?
|
# FIXME Those are really usefull ?
|
||||||
|
@ -1807,7 +1886,6 @@ def backup_delete(name, repository):
|
||||||
logger.success(m18n.n("backup_deleted"))
|
logger.success(m18n.n("backup_deleted"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Repository subcategory
|
# Repository subcategory
|
||||||
#
|
#
|
||||||
|
@ -1817,7 +1895,8 @@ def backup_repository_list(full=False):
|
||||||
"""
|
"""
|
||||||
List available repositories where put archives
|
List available repositories where put archives
|
||||||
"""
|
"""
|
||||||
return { "repositories": BackupRepository.list(full) }
|
|
||||||
|
return {"repositories": BackupRepository.list(full)}
|
||||||
|
|
||||||
|
|
||||||
def backup_repository_info(shortname, space_used=False):
|
def backup_repository_info(shortname, space_used=False):
|
||||||
|
@ -1833,11 +1912,13 @@ def backup_repository_add(operation_logger, shortname, name=None, location=None,
|
||||||
"""
|
"""
|
||||||
args = {k: v for k, v in locals().items() if v is not None}
|
args = {k: v for k, v in locals().items() if v is not None}
|
||||||
repository = BackupRepository(shortname, creation=True)
|
repository = BackupRepository(shortname, creation=True)
|
||||||
|
|
||||||
return repository.set(
|
return repository.set(
|
||||||
operation_logger=args.pop('operation_logger')
|
operation_logger=args.pop('operation_logger'),
|
||||||
args=urllib.parse.urlencode(args),
|
args=urllib.parse.urlencode(args)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def backup_repository_update(operation_logger, shortname, name=None,
|
def backup_repository_update(operation_logger, shortname, name=None,
|
||||||
quota=None, passphrase=None,
|
quota=None, passphrase=None,
|
||||||
|
@ -1848,6 +1929,7 @@ def backup_repository_update(operation_logger, shortname, name=None,
|
||||||
|
|
||||||
backup_repository_add(creation=False, **locals())
|
backup_repository_add(creation=False, **locals())
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def backup_repository_remove(operation_logger, shortname, purge=False):
|
def backup_repository_remove(operation_logger, shortname, purge=False):
|
||||||
"""
|
"""
|
||||||
|
@ -1877,11 +1959,10 @@ class BackupTimer(ConfigPanel):
|
||||||
self.values = self._get_default_values()
|
self.values = self._get_default_values()
|
||||||
|
|
||||||
if os.path.exists(self.save_path) and os.path.isfile(self.save_path):
|
if os.path.exists(self.save_path) and os.path.isfile(self.save_path):
|
||||||
raise NotImplementedError() # TODO
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
if os.path.exists(self.service_path) and os.path.isfile(self.service_path):
|
if os.path.exists(self.service_path) and os.path.isfile(self.service_path):
|
||||||
raise NotImplementedError() # TODO
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
|
||||||
def _apply(self, values):
|
def _apply(self, values):
|
||||||
write_to_file(self.save_path, f"""[Unit]
|
write_to_file(self.save_path, f"""[Unit]
|
||||||
|
@ -1911,7 +1992,7 @@ def backup_timer_list(full=False):
|
||||||
"""
|
"""
|
||||||
List all backup timer
|
List all backup timer
|
||||||
"""
|
"""
|
||||||
return { "backup_timer": BackupTimer.list(full) }
|
return {"backup_timer": BackupTimer.list(full)}
|
||||||
|
|
||||||
|
|
||||||
def backup_timer_info(shortname, space_used=False):
|
def backup_timer_info(shortname, space_used=False):
|
||||||
|
@ -1921,7 +2002,7 @@ def backup_timer_info(shortname, space_used=False):
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def backup_timer_add(
|
def backup_timer_add(
|
||||||
operation_logger,
|
operation_logger,
|
||||||
name=None,
|
shortname=None,
|
||||||
description=None,
|
description=None,
|
||||||
repos=[],
|
repos=[],
|
||||||
system=[],
|
system=[],
|
||||||
|
@ -1939,19 +2020,21 @@ def backup_timer_add(
|
||||||
args = {k: v for k, v in locals().items() if v is not None}
|
args = {k: v for k, v in locals().items() if v is not None}
|
||||||
timer = BackupTimer(shortname, creation=True)
|
timer = BackupTimer(shortname, creation=True)
|
||||||
return timer.set(
|
return timer.set(
|
||||||
operation_logger=args.pop('operation_logger')
|
operation_logger=args.pop('operation_logger'),
|
||||||
args=urllib.parse.urlencode(args),
|
args=urllib.parse.urlencode(args)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def backup_timer_update(operation_logger, shortname, name=None,
|
def backup_timer_update(operation_logger, shortname, name=None,
|
||||||
quota=None, passphrase=None,
|
quota=None, passphrase=None,
|
||||||
alert=None, alert_delay=None):
|
alert=None, alert_delay=None):
|
||||||
"""
|
"""
|
||||||
Update a backup timer
|
Update a backup timer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
backup_timer_add(creation=False, **locals()):
|
backup_timer_add(creation=False, **locals())
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def backup_timer_remove(operation_logger, shortname, purge=False):
|
def backup_timer_remove(operation_logger, shortname, purge=False):
|
||||||
|
@ -1961,7 +2044,6 @@ def backup_timer_remove(operation_logger, shortname, purge=False):
|
||||||
BackupTimer(shortname).remove(purge)
|
BackupTimer(shortname).remove(purge)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Misc helpers #
|
# Misc helpers #
|
||||||
#
|
#
|
||||||
|
@ -1969,6 +2051,7 @@ def backup_timer_remove(operation_logger, shortname, purge=False):
|
||||||
|
|
||||||
def _create_archive_dir():
|
def _create_archive_dir():
|
||||||
"""Create the YunoHost archives directory if doesn't exist"""
|
"""Create the YunoHost archives directory if doesn't exist"""
|
||||||
|
|
||||||
if not os.path.isdir(ARCHIVES_PATH):
|
if not os.path.isdir(ARCHIVES_PATH):
|
||||||
if os.path.lexists(ARCHIVES_PATH):
|
if os.path.lexists(ARCHIVES_PATH):
|
||||||
raise YunohostError("backup_output_symlink_dir_broken", path=ARCHIVES_PATH)
|
raise YunohostError("backup_output_symlink_dir_broken", path=ARCHIVES_PATH)
|
||||||
|
@ -1980,10 +2063,12 @@ def _create_archive_dir():
|
||||||
|
|
||||||
def _call_for_each_path(self, callback, csv_path=None):
|
def _call_for_each_path(self, callback, csv_path=None):
|
||||||
"""Call a callback for each path in csv"""
|
"""Call a callback for each path in csv"""
|
||||||
|
|
||||||
if csv_path is None:
|
if csv_path is None:
|
||||||
csv_path = self.csv_path
|
csv_path = self.csv_path
|
||||||
with open(csv_path, "r") as backup_file:
|
with open(csv_path, "r") as backup_file:
|
||||||
backup_csv = csv.DictReader(backup_file, fieldnames=["source", "dest"])
|
backup_csv = csv.DictReader(backup_file, fieldnames=["source", "dest"])
|
||||||
|
|
||||||
for row in backup_csv:
|
for row in backup_csv:
|
||||||
callback(self, row["source"], row["dest"])
|
callback(self, row["source"], row["dest"])
|
||||||
|
|
||||||
|
@ -1999,17 +2084,21 @@ def _recursive_umount(directory):
|
||||||
|
|
||||||
points_to_umount = [
|
points_to_umount = [
|
||||||
line.split(" ")[2]
|
line.split(" ")[2]
|
||||||
|
|
||||||
for line in mount_lines
|
for line in mount_lines
|
||||||
|
|
||||||
if len(line) >= 3 and line.split(" ")[2].startswith(os.path.realpath(directory))
|
if len(line) >= 3 and line.split(" ")[2].startswith(os.path.realpath(directory))
|
||||||
]
|
]
|
||||||
|
|
||||||
everything_went_fine = True
|
everything_went_fine = True
|
||||||
|
|
||||||
for point in reversed(points_to_umount):
|
for point in reversed(points_to_umount):
|
||||||
ret = subprocess.call(["umount", point])
|
ret = subprocess.call(["umount", point])
|
||||||
|
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
everything_went_fine = False
|
everything_went_fine = False
|
||||||
logger.warning(m18n.n("backup_cleaning_failed", point))
|
logger.warning(m18n.n("backup_cleaning_failed", point))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return everything_went_fine
|
return everything_went_fine
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue