mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
[fix] Backup timer
This commit is contained in:
parent
97ce3f188b
commit
ad5c0f0cc6
3 changed files with 171 additions and 43 deletions
|
@ -1264,6 +1264,19 @@ backup:
|
|||
--purge:
|
||||
help: Remove all archives and data inside repository
|
||||
action: store_true
|
||||
|
||||
### backup_repository_prune()
|
||||
prune:
|
||||
action_help: Prune archives in a backup repository
|
||||
api: POST /backups/repository/<shortname>/prune
|
||||
arguments:
|
||||
shortname:
|
||||
help: Name of the backup repository to prune
|
||||
extra:
|
||||
pattern: *pattern_backup_repository_shortname
|
||||
--prefix:
|
||||
help: Prefix on which we prune
|
||||
nargs: "?"
|
||||
timer:
|
||||
subcategory_help: Manage backup timer
|
||||
actions:
|
||||
|
@ -1272,6 +1285,10 @@ backup:
|
|||
list:
|
||||
action_help: List backup timer
|
||||
api: GET /backup/timer
|
||||
arguments:
|
||||
--full:
|
||||
help: Show more details
|
||||
action: store_true
|
||||
|
||||
### backup_timer_add()
|
||||
add:
|
||||
|
@ -1365,6 +1382,16 @@ backup:
|
|||
extra:
|
||||
pattern: *pattern_backup_timer_name
|
||||
|
||||
### backup_timer_run()
|
||||
run:
|
||||
action_help: Run a backup timer
|
||||
api: POST /backup/timer/<name>/run
|
||||
arguments:
|
||||
name:
|
||||
help: Backup timer to run
|
||||
extra:
|
||||
pattern: *pattern_backup_timer_name
|
||||
|
||||
#############################
|
||||
# Settings #
|
||||
#############################
|
||||
|
|
185
src/backup.py
185
src/backup.py
|
@ -1954,7 +1954,7 @@ def backup_repository_add(operation_logger, shortname, name=None, location=None,
|
|||
|
||||
return repository.set(
|
||||
operation_logger=args.pop('operation_logger'),
|
||||
args=urllib.parse.urlencode(args)
|
||||
args=urllib.parse.urlencode(args, doseq=True)
|
||||
)
|
||||
|
||||
|
||||
|
@ -1977,6 +1977,20 @@ def backup_repository_remove(operation_logger, shortname, purge=False):
|
|||
BackupRepository(shortname).remove(purge)
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def backup_repository_prune(operation_logger, shortname, prefix=None, keep_hourly=0, keep_daily=10, keep_weekly=8, keep_monthly=8):
|
||||
"""
|
||||
Remove a backup repository
|
||||
"""
|
||||
BackupRepository(shortname).prune(
|
||||
prefix=prefix,
|
||||
keep_hourly=keep_hourly,
|
||||
keep_daily=keep_daily,
|
||||
keep_weekly=keep_weekly,
|
||||
keep_monthly=keep_monthly,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Timer subcategory
|
||||
#
|
||||
|
@ -1986,45 +2000,15 @@ class BackupTimer(ConfigPanel):
|
|||
BackupRepository manage all repository the admin added to the instance
|
||||
"""
|
||||
entity_type = "backup_timer"
|
||||
save_path_tpl = "/etc/systemd/system/backup_timer_{entity}.timer"
|
||||
timer_name_tpl = "backup_{entity}"
|
||||
save_path_tpl = "/etc/yunohost/backup/timer/{entity}.yml"
|
||||
timer_path_tpl = "/etc/systemd/system/{timer_name}.timer"
|
||||
service_path_tpl = "/etc/systemd/system/{timer_name}.service"
|
||||
save_mode = "full"
|
||||
|
||||
@property
|
||||
def service_path(self):
|
||||
return self.save_path[:-len(".timer")] + ".service"
|
||||
# TODO prefill apps and system question with good values
|
||||
# TODO validate calendar entry
|
||||
|
||||
def _load_current_values(self):
|
||||
# Inject defaults if needed (using the magic .update() ;))
|
||||
self.values = self._get_default_values()
|
||||
|
||||
if os.path.exists(self.save_path) and os.path.isfile(self.save_path):
|
||||
raise NotImplementedError() # TODO
|
||||
|
||||
if os.path.exists(self.service_path) and os.path.isfile(self.service_path):
|
||||
raise NotImplementedError() # TODO
|
||||
|
||||
def _apply(self):
|
||||
write_to_file(self.save_path, f"""[Unit]
|
||||
Description=Run backup {self.entity} regularly
|
||||
|
||||
[Timer]
|
||||
OnCalendar={self.values['schedule']}
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
""")
|
||||
# TODO --system and --apps params
|
||||
# TODO prune params
|
||||
write_to_file(self.service_path, f"""[Unit]
|
||||
Description=Run backup {self.entity}
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/yunohost backup create -n '{self.entity}' -r '{self.repositories}' --system --apps ; /usr/bin/yunohost backup prune -n '{self.entity}'
|
||||
User=root
|
||||
Group=root
|
||||
""")
|
||||
@classmethod
|
||||
def list(cls, full=False):
|
||||
"""
|
||||
|
@ -2038,12 +2022,121 @@ Group=root
|
|||
full_timers = {}
|
||||
for timer in timers:
|
||||
try:
|
||||
full_timers.update(BackupTimer(timer).info())
|
||||
full_timers[timer] = BackupTimer(timer).info()
|
||||
except Exception as e:
|
||||
logger.error(f"Unable to open timer {timer}: {e}")
|
||||
|
||||
return full_timers
|
||||
|
||||
@property
|
||||
def timer_name(self):
|
||||
return self.timer_name_tpl.format(entity=self.entity)
|
||||
|
||||
@property
|
||||
def service_path(self):
|
||||
return self.service_path_tpl.format(timer_name=self.timer_name)
|
||||
|
||||
@property
|
||||
def timer_path(self):
|
||||
return self.timer_path_tpl.format(timer_name=self.timer_name)
|
||||
|
||||
def _reload_systemd(self):
|
||||
try:
|
||||
check_output("systemctl daemon-reload")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to reload daemon : {e}")
|
||||
|
||||
def _run_service_command(self, action, *args):
|
||||
# TODO improve services to support timers
|
||||
# See https://github.com/YunoHost/issues/issues/1519
|
||||
try:
|
||||
check_output(f"systemctl {action} {self.timer_name}.timer")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to {action} {self.timer_name}.timer : {e}")
|
||||
|
||||
def _load_current_values(self):
|
||||
super()._load_current_values()
|
||||
|
||||
# Search OnCalendar schedule property
|
||||
if os.path.exists(self.timer_path) and os.path.isfile(self.timer_path):
|
||||
with open(self.timer_path, 'r') as f:
|
||||
for index, line in enumerate(f):
|
||||
if line.startswith("OnCalendar="):
|
||||
self.values["schedule"] = line[11:].strip()
|
||||
break
|
||||
else:
|
||||
logger.debug(f"No OnCalendar property found in {self.timer_path}")
|
||||
|
||||
def _apply(self):
|
||||
|
||||
super()._apply()
|
||||
|
||||
# TODO Add RandomizedDelaySec for daily and other special event
|
||||
write_to_file(self.timer_path, f"""[Unit]
|
||||
Description=Run backup {self.entity} regularly
|
||||
|
||||
[Timer]
|
||||
OnCalendar={self.values['schedule']}
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
""")
|
||||
write_to_file(self.service_path, f"""[Unit]
|
||||
Description=Run backup {self.entity}
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/yunohost backup timer run '{self.entity}'
|
||||
User=root
|
||||
Group=root
|
||||
""")
|
||||
self._reload_systemd()
|
||||
self._run_service_command("reset-failed")
|
||||
self.start()
|
||||
|
||||
def info(self):
|
||||
return self.get(mode="export")
|
||||
|
||||
def remove(self):
|
||||
|
||||
self.stop()
|
||||
rm(self.save_path, force=True)
|
||||
rm(self.service_path, force=True)
|
||||
rm(self.timer_path, force=True)
|
||||
self._reload_systemd()
|
||||
self._run_service_command("reset-failed")
|
||||
logger.success(m18n.n("backup_timer_removed", timer=self.entity))
|
||||
|
||||
def start(self):
|
||||
self._run_service_command("enable")
|
||||
self._run_service_command("start")
|
||||
|
||||
def stop(self):
|
||||
self._run_service_command("stop")
|
||||
self._run_service_command("disable")
|
||||
|
||||
def run(self, operation_logger):
|
||||
self._load_current_values()
|
||||
backup_create(
|
||||
operation_logger,
|
||||
name=self.entity,
|
||||
description=self.description,
|
||||
repositories=self.repositories,
|
||||
system=self.system,
|
||||
apps=self.apps
|
||||
)
|
||||
for repository in self.repositories:
|
||||
backup_repository_prune(
|
||||
operation_logger,
|
||||
shortname=repository,
|
||||
prefix=self.entity,
|
||||
keep_hourly=self.keep_hourly,
|
||||
keep_daily=self.keep_daily,
|
||||
keep_weekly=self.keep_weekly,
|
||||
keep_monthly=self.keep_monthly,
|
||||
)
|
||||
|
||||
|
||||
def backup_timer_list(full=False):
|
||||
"""
|
||||
|
@ -2053,7 +2146,7 @@ def backup_timer_list(full=False):
|
|||
|
||||
|
||||
def backup_timer_info(name):
|
||||
return BackupTimer(name).get()
|
||||
return BackupTimer(name).info()
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
|
@ -2079,7 +2172,7 @@ def backup_timer_add(
|
|||
timer = BackupTimer(name, creation=True)
|
||||
return timer.set(
|
||||
operation_logger=args.pop('operation_logger'),
|
||||
args=urllib.parse.urlencode(args)
|
||||
args=urllib.parse.urlencode(args, doseq=True)
|
||||
)
|
||||
|
||||
|
||||
|
@ -2095,11 +2188,19 @@ def backup_timer_update(operation_logger, shortname, name=None,
|
|||
|
||||
|
||||
@is_unit_operation()
|
||||
def backup_timer_remove(operation_logger, shortname, purge=False):
|
||||
def backup_timer_remove(operation_logger, name):
|
||||
"""
|
||||
Remove a backup timer
|
||||
"""
|
||||
BackupTimer(shortname).remove(purge)
|
||||
BackupTimer(name).remove()
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def backup_timer_run(operation_logger, name):
|
||||
"""
|
||||
Run a backup timer
|
||||
"""
|
||||
BackupTimer(name).run(operation_logger)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -46,7 +46,7 @@ from yunohost.utils.system import disk_usage, binary_to_human
|
|||
from yunohost.utils.network import get_ssh_public_key, SHF_BASE_URL
|
||||
|
||||
logger = getActionLogger('yunohost.repository')
|
||||
REPOSITORIES_DIR = '/etc/yunohost/repositories'
|
||||
REPOSITORIES_DIR = '/etc/yunohost/backup/repositories'
|
||||
CACHE_INFO_DIR = "/var/cache/yunohost/repositories/{repository}"
|
||||
REPOSITORY_CONFIG_PATH = "/usr/share/yunohost/other/config_repository.toml"
|
||||
MB_ALLOWED_TO_ORGANIZE = 10
|
||||
|
|
Loading…
Add table
Reference in a new issue