[fix] Validate backup/restore hooks and show restoration result

This commit is contained in:
Jérôme Lebleu 2015-10-05 17:43:48 +02:00
parent 76c7b3b3db
commit a51e395bca
2 changed files with 90 additions and 25 deletions

View file

@ -62,7 +62,7 @@ def backup_create(name=None, description=None, output_directory=None,
""" """
# TODO: Add a 'clean' argument to clean output directory # TODO: Add a 'clean' argument to clean output directory
from yunohost.hook import hook_callback, hook_exec from yunohost.hook import hook_list, hook_callback, hook_exec
tmp_dir = None tmp_dir = None
@ -136,9 +136,24 @@ def backup_create(name=None, description=None, output_directory=None,
# Run system hooks # Run system hooks
if not ignore_hooks: if not ignore_hooks:
msignals.display(m18n.n('backup_running_hooks')) # Check hooks availibility
hooks_ret = hook_callback('backup', hooks, args=[tmp_dir]) hooks_available = hook_list('backup')['hooks']
info['hooks'] = hooks_ret['succeed'] hooks_filtered = set()
if hooks:
for hook in hooks:
if hook not in hooks_available:
logger.exception("backup hook '%s' not found", hook)
msignals.display(m18n.n('backup_hook_unknown', hook=hook),
'error')
else:
hooks_filtered.add(hook)
else:
hooks_filtered = hooks_available
if hooks_filtered:
msignals.display(m18n.n('backup_running_hooks'))
ret = hook_callback('backup', hooks_filtered, args=[tmp_dir])
info['hooks'] = ret['succeed']
# Backup apps # Backup apps
if not ignore_apps: if not ignore_apps:
@ -205,10 +220,10 @@ def backup_create(name=None, description=None, output_directory=None,
finally: finally:
filesystem.rm(tmp_script, force=True) filesystem.rm(tmp_script, force=True)
# Check if something has been saved # Check if something has been saved
if ignore_hooks and not info['apps']: if not info['hooks'] and not info['apps']:
_clean_tmp_dir(1) _clean_tmp_dir(1)
raise MoulinetteError(errno.EINVAL, m18n.n('backup_nothings_done')) raise MoulinetteError(errno.EINVAL, m18n.n('backup_nothings_done'))
# Create backup info file # Create backup info file
with open("%s/info.json" % tmp_dir, 'w') as f: with open("%s/info.json" % tmp_dir, 'w') as f:
@ -270,9 +285,12 @@ def backup_restore(name, hooks=[], apps=[], ignore_apps=False, ignore_hooks=Fals
force -- Force restauration on an already installed system force -- Force restauration on an already installed system
""" """
from yunohost.hook import hook_add from yunohost.hook import hook_add, hook_list, hook_callback, hook_exec
from yunohost.hook import hook_callback
from yunohost.hook import hook_exec # Validate what to restore
if ignore_hooks and ignore_apps:
raise MoulinetteError(errno.EINVAL,
m18n.n('restore_action_required'))
# Retrieve and open the archive # Retrieve and open the archive
info = backup_info(name) info = backup_info(name)
@ -291,6 +309,13 @@ def backup_restore(name, hooks=[], apps=[], ignore_apps=False, ignore_hooks=Fals
tmp_dir) tmp_dir)
os.system('rm -rf %s' % tmp_dir) os.system('rm -rf %s' % tmp_dir)
def _clean_tmp_dir(retcode=0):
ret = hook_callback('post_backup_restore', args=[tmp_dir, retcode])
if not ret['failed']:
filesystem.rm(tmp_dir, True, True)
else:
msignals.display(m18n.n('restore_cleaning_failed'), 'warning')
# Extract the tarball # Extract the tarball
msignals.display(m18n.n('backup_extracting_archive')) msignals.display(m18n.n('backup_extracting_archive'))
tar.extractall(tmp_dir) tar.extractall(tmp_dir)
@ -308,6 +333,12 @@ def backup_restore(name, hooks=[], apps=[], ignore_apps=False, ignore_hooks=Fals
logger.info("restoring from backup '%s' created on %s", name, logger.info("restoring from backup '%s' created on %s", name,
time.ctime(info['created_at'])) time.ctime(info['created_at']))
# Initialize restauration summary result
result = {
'apps': [],
'hooks': {},
}
# Check if YunoHost is installed # Check if YunoHost is installed
if os.path.isfile('/etc/yunohost/installed'): if os.path.isfile('/etc/yunohost/installed'):
msignals.display(m18n.n('yunohost_already_installed'), 'warning') msignals.display(m18n.n('yunohost_already_installed'), 'warning')
@ -338,18 +369,40 @@ def backup_restore(name, hooks=[], apps=[], ignore_apps=False, ignore_hooks=Fals
logger.info("executing the post-install...") logger.info("executing the post-install...")
tools_postinstall(domain, 'yunohost', True) tools_postinstall(domain, 'yunohost', True)
# Run hooks # Run system hooks
if not ignore_hooks: if not ignore_hooks:
if hooks is None or len(hooks)==0: # Filter hooks to execute
hooks=info['hooks'].keys() hooks_list = set(info['hooks'].keys())
_is_hook_in_backup = lambda h: True
if hooks:
def _is_hook_in_backup(h):
if h in hooks_list:
return True
logger.warning("hook '%s' not executed in the backup '%s'",
h, archive_file)
msignals.display(m18n.n('backup_archive_hook_not_exec', hook=h),
'error')
return False
else:
hooks = hooks_list
hooks_filtered=list(set(hooks) & set(info['hooks'].keys())) # Check hooks availibility
hooks_unexecuted=set(hooks) - set(info['hooks'].keys()) hooks_available = hook_list('restore')['hooks']
for hook in hooks_unexecuted: hooks_filtered = set()
logger.warning("hook '%s' not in this backup", hook) for hook in hooks:
msignals.display(m18n.n('backup_hook_unavailable', hook), 'warning') if not _is_hook_in_backup(hook):
msignals.display(m18n.n('restore_running_hooks')) continue
hook_callback('restore', hooks_filtered, args=[tmp_dir]) if hook not in hooks_available:
logger.exception("restoration hook '%s' not found", hook)
msignals.display(m18n.n('restore_hook_unavailable', hook=hook),
'error')
continue
hooks_filtered.add(hook)
if hooks_filtered:
msignals.display(m18n.n('restore_running_hooks'))
ret = hook_callback('restore', hooks_filtered, args=[tmp_dir])
result['hooks'] = ret['succeed']
# Add apps restore hook # Add apps restore hook
if not ignore_apps: if not ignore_apps:
@ -409,14 +462,21 @@ def backup_restore(name, hooks=[], apps=[], ignore_apps=False, ignore_hooks=Fals
'error') 'error')
# Cleaning app directory # Cleaning app directory
shutil.rmtree(app_setting_path, ignore_errors=True) shutil.rmtree(app_setting_path, ignore_errors=True)
else:
result['apps'].append(app_id)
finally: finally:
filesystem.rm(tmp_script, force=True) filesystem.rm(tmp_script, force=True)
# Remove temporary directory # Check if something has been restored
os.system('rm -rf %s' % tmp_dir) if not result['hooks'] and not result['apps']:
_clean_tmp_dir(1)
raise MoulinetteError(errno.EINVAL, m18n.n('restore_nothings_done'))
_clean_tmp_dir()
msignals.display(m18n.n('restore_complete'), 'success') msignals.display(m18n.n('restore_complete'), 'success')
return result
def backup_list(with_info=False, human_readable=False): def backup_list(with_info=False, human_readable=False):
""" """

View file

@ -144,6 +144,7 @@
"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_directory_forbidden" : "Forbidden output directory", "backup_output_directory_forbidden" : "Forbidden output directory",
"backup_output_directory_not_empty" : "Output directory is not empty", "backup_output_directory_not_empty" : "Output directory is not empty",
"backup_hook_unknown" : "Backup hook '{hook:s}' unknown",
"backup_running_hooks" : "Running backup hooks...", "backup_running_hooks" : "Running backup hooks...",
"backup_running_app_script" : "Running backup script of app '{:s}'...", "backup_running_app_script" : "Running backup script of app '{:s}'...",
"backup_creating_archive" : "Creating the backup archive...", "backup_creating_archive" : "Creating the backup archive...",
@ -151,18 +152,22 @@
"backup_archive_open_failed" : "Unable to open the backup archive", "backup_archive_open_failed" : "Unable to open the backup archive",
"backup_archive_name_unknown" : "Unknown local backup archive named '{:s}'", "backup_archive_name_unknown" : "Unknown local backup archive named '{:s}'",
"backup_archive_name_exists" : "Backup archive name already exists", "backup_archive_name_exists" : "Backup archive name already exists",
"backup_archive_hook_not_exec" : "Hook '{hook:s}' not executed in this backup",
"backup_archive_app_not_found" : "App '{app:s}' not found in the backup archive", "backup_archive_app_not_found" : "App '{app:s}' not found in the backup archive",
"backup_app_failed" : "Unable to back up the app '{app:s}'", "backup_app_failed" : "Unable to back up the app '{app:s}'",
"backup_nothings_done" : "There is nothing to save", "backup_nothings_done" : "There is nothing to save",
"backup_cleaning_failed" : "Unable to clean backup directory", "backup_cleaning_failed" : "Unable to clean backup temporary directory",
"backup_complete" : "Backup complete", "backup_complete" : "Backup complete",
"backup_invalid_archive" : "Invalid backup archive", "backup_invalid_archive" : "Invalid backup archive",
"backup_hook_unavailable" : "The hook '{:s}' is not in this backup", "restore_action_required" : "You must specify something to restore",
"restore_confirm_yunohost_installed" : "Do you really want to restore an already installed system? [{answers:s}]", "restore_confirm_yunohost_installed" : "Do you really want to restore an already installed system? [{answers:s}]",
"restore_hook_unavailable" : "Restauration hook '{hook:s}' not available on your system",
"restore_app_failed" : "Unable to restore the app '{app:s}'", "restore_app_failed" : "Unable to restore the app '{app:s}'",
"restore_running_hooks" : "Running restoration hooks...", "restore_running_hooks" : "Running restoration hooks...",
"restore_running_app_script" : "Running restore script of app '{app:s}'...", "restore_running_app_script" : "Running restore script of app '{app:s}'...",
"restore_failed" : "Unable to restore the system", "restore_failed" : "Unable to restore the system",
"restore_nothings_done" : "Nothing has been restored",
"restore_cleaning_failed" : "Unable to clean restoration temporary directory",
"restore_complete" : "Restore complete", "restore_complete" : "Restore complete",
"restore_already_installed_app": "An app is already installed with the id '{app:s}'", "restore_already_installed_app": "An app is already installed with the id '{app:s}'",
"unbackup_app" : "App '{app:s}' will not be saved", "unbackup_app" : "App '{app:s}' will not be saved",