[fix] Repository list, add, remove

This commit is contained in:
ljf 2022-10-11 02:24:05 +02:00
parent 6eca4bff69
commit 04f85eb860
No known key found for this signature in database
5 changed files with 49 additions and 32 deletions

View file

@ -90,7 +90,8 @@
"backup_archive_system_part_not_available": "System part '{part}' unavailable in this backup", "backup_archive_system_part_not_available": "System part '{part}' unavailable in this backup",
"backup_archive_writing_error": "Could not add the files '{source}' (named in the archive '{dest}') to be backed up into the compressed archive '{archive}'", "backup_archive_writing_error": "Could not add the files '{source}' (named in the archive '{dest}') to be backed up into the compressed archive '{archive}'",
"backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size}MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size}MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)",
"backup_borg_init_error": "Unable initialize the borg repository", "backup_borg_init_error": "Unable initialize the borg repository: {error}",
"backup_borg_already_initialized": "The borg repository '{repository}' already exists, it has been properly added to repositories managed by YunoHost cli.",
"backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected",
"backup_cleaning_failed": "Could not clean up the temporary backup folder", "backup_cleaning_failed": "Could not clean up the temporary backup folder",
"backup_copying_to_organize_the_archive": "Copying {size}MB to organize the archive", "backup_copying_to_organize_the_archive": "Copying {size}MB to organize the archive",
@ -116,6 +117,8 @@
"backup_output_directory_required": "You must provide an output directory for the backup", "backup_output_directory_required": "You must provide an output directory for the backup",
"backup_output_symlink_dir_broken": "Your archive directory '{path}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", "backup_output_symlink_dir_broken": "Your archive directory '{path}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.",
"backup_permission": "Backup permission for {app}", "backup_permission": "Backup permission for {app}",
"backup_repository_exists": "Backup repository '{backup_repository}' already exists",
"backup_repository_unknown": "Backup repository '{backup_repository}' unknown",
"backup_running_hooks": "Running backup hooks...", "backup_running_hooks": "Running backup hooks...",
"backup_system_part_failed": "Could not backup the '{part}' system part", "backup_system_part_failed": "Could not backup the '{part}' system part",
"backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive", "backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive",

View file

@ -1099,6 +1099,9 @@ backup:
--full: --full:
help: Show more details help: Show more details
action: store_true action: store_true
--space-used:
help: Display size used
action: store_true
### backup_repository_info() ### backup_repository_info()
info: info:
@ -1111,13 +1114,9 @@ backup:
pattern: &pattern_backup_repository_shortname pattern: &pattern_backup_repository_shortname
- !!str ^[a-zA-Z0-9-_\.]+$ - !!str ^[a-zA-Z0-9-_\.]+$
- "pattern_backup_repository_shortname" - "pattern_backup_repository_shortname"
-H:
full: --human-readable
help: Print sizes in human readable format
action: store_true
--space-used: --space-used:
help: Display size used help: Display size used
action: store_false action: store_true
### backup_repository_add() ### backup_repository_add()
add: add:

View file

@ -1871,12 +1871,12 @@ def backup_delete(name, repository):
# #
def backup_repository_list(full=False): def backup_repository_list(space_used=False, full=False):
""" """
List available repositories where put archives List available repositories where put archives
""" """
return {"repositories": BackupRepository.list(full)} return {"repositories": BackupRepository.list(space_used, full)}
def backup_repository_info(shortname, space_used=False): def backup_repository_info(shortname, space_used=False):

View file

@ -24,6 +24,7 @@ import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from moulinette import m18n
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
@ -37,7 +38,7 @@ class BorgBackupRepository(LocalBackupRepository):
method_name = "borg" method_name = "borg"
# TODO logs # TODO logs
def _run_borg_command(self, cmd, stdout=None): def _run_borg_command(self, cmd, stdout=None, stderr=None):
""" Call a submethod of borg with the good context """ Call a submethod of borg with the good context
""" """
env = dict(os.environ) env = dict(os.environ)
@ -59,15 +60,16 @@ class BorgBackupRepository(LocalBackupRepository):
# Authorize to move the repository (borgbase do this) # Authorize to move the repository (borgbase do this)
env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = "yes" env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = "yes"
return subprocess.Popen(cmd, env=env, stdout=stdout) return subprocess.Popen(cmd, env=env,
stdout=stdout, stderr=stderr)
def _call(self, action, cmd, json_output=False): def _call(self, action, cmd, json_output=False):
borg = self._run_borg_command(cmd) borg = self._run_borg_command(cmd, stdout=subprocess.PIPE,
return_code = borg.wait() stderr=subprocess.PIPE)
if return_code: out, err = borg.communicate()
raise YunohostError(f"backup_borg_{action}_error") if borg.returncode:
raise YunohostError(f"backup_borg_{action}_error", error=err)
out, _ = borg.communicate()
if json_output: if json_output:
try: try:
return json.loads(out) return json.loads(out)
@ -117,8 +119,20 @@ class BorgBackupRepository(LocalBackupRepository):
if "quota" in self.future_values and self.future_values["quota"]: if "quota" in self.future_values and self.future_values["quota"]:
cmd += ['--storage-quota', self.quota] cmd += ['--storage-quota', self.quota]
try:
self._call('init', cmd) self._call('init', cmd)
except YunohostError as e:
if e.key != "backup_borg_init_error":
raise
else:
# Check if it's possible to read the borg repo with current settings
try:
cmd = ["borg", "info", self.location]
self._call('info', cmd)
except YunohostError:
raise e
logger.info(m18n.n("backup_borg_already_initialized", repository=self.location))
def update(self): def update(self):
raise NotImplementedError() raise NotImplementedError()
@ -148,11 +162,10 @@ class BorgBackupRepository(LocalBackupRepository):
return [archive["name"] for archive in response['archives']] return [archive["name"] for archive in response['archives']]
def compute_space_used(self): def compute_space_used(self):
if not self.is_remote: """ Return the size of this repo on the disk"""
return super().purge() # FIXME this size could be unrelevant, comparison between du and borg sizes doesn't match !
else:
cmd = ["borg", "info", "--json", self.location] cmd = ["borg", "info", "--json", self.location]
response = self._call('info', cmd) response = self._call('info', cmd, json_output=True)
return response["cache"]["stats"]["unique_size"] return response["cache"]["stats"]["unique_size"]
def prune(self, prefix=None, **kwargs): def prune(self, prefix=None, **kwargs):

View file

@ -104,13 +104,14 @@ class BackupRepository(ConfigPanel):
if not full: if not full:
return repositories return repositories
full_repositories = {}
for repo in repositories: for repo in repositories:
try: try:
repositories[repo] = BackupRepository(repo).info(space_used) full_repositories.update(BackupRepository(repo).info(space_used))
except Exception: except Exception as e:
logger.error(f"Unable to open repository {repo}") logger.error(f"Unable to open repository {repo}: {e}")
return repositories return full_repositories
# ================================================= # =================================================
# Config Panel Hooks # Config Panel Hooks
@ -232,18 +233,19 @@ class BackupRepository(ConfigPanel):
self.purge() self.purge()
rm(self.save_path, force=True) rm(self.save_path, force=True)
logger.success(m18n.n("repository_removed", repository=self.shortname)) logger.success(m18n.n("repository_removed", repository=self.entity))
def info(self, space_used=False): def info(self, space_used=False):
result = super().get(mode="export") result = super().get(mode="export")
if self.__class__ == BackupRepository and space_used is True: if space_used is True:
result["space_used"] = self.compute_space_used() result["space_used"] = self.compute_space_used()
return {self.shortname: result} return {self.entity: result}
def list_archives(self, with_info): def list_archives(self, with_info):
archives = self.list_archive_name() self._cast_by_method()
archives = self.list_archives_names()
if with_info: if with_info:
d = {} d = {}
for archive in archives: for archive in archives:
@ -267,7 +269,7 @@ class BackupRepository(ConfigPanel):
# List archives with creation date # List archives with creation date
archives = {} archives = {}
for archive_name in self.list_archive_name(prefix): for archive_name in self.list_archives_names(prefix):
archive = BackupArchive(repo=self, name=archive_name) archive = BackupArchive(repo=self, name=archive_name)
created_at = archive.info()["created_at"] created_at = archive.info()["created_at"]
archives[created_at] = archive archives[created_at] = archive
@ -314,7 +316,7 @@ class BackupRepository(ConfigPanel):
def purge(self): def purge(self):
raise NotImplementedError() raise NotImplementedError()
def list_archives_names(self): def list_archives_names(self, prefix=None):
raise NotImplementedError() raise NotImplementedError()
def compute_space_used(self): def compute_space_used(self):