mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[fix] Repository list, add, remove
This commit is contained in:
parent
6eca4bff69
commit
04f85eb860
5 changed files with 49 additions and 32 deletions
|
@ -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",
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Add table
Reference in a new issue