[enh] Add 'output_directory' and 'no_compress' arguments to backup_create

This commit is contained in:
Jérôme Lebleu 2014-11-17 10:59:43 +01:00
parent 42b1189cd1
commit f7b591840c
4 changed files with 107 additions and 44 deletions

View file

@ -574,6 +574,13 @@ backup:
-d: -d:
full: --description full: --description
help: Short description of the backup help: Short description of the backup
-o:
full: --output-directory
help: Output directory for the backup
-r:
full: --no-compress
help: Do not create an archive file
action: store_true
--ignore-apps: --ignore-apps:
help: Do not backup apps help: Do not backup apps
action: store_true action: store_true

View file

@ -24,6 +24,7 @@
Manage backups Manage backups
""" """
import os import os
import re
import sys import sys
import json import json
import errno import errno
@ -40,36 +41,70 @@ archives_path = '%s/archives' % backup_path
logger = getActionLogger('yunohost.backup') logger = getActionLogger('yunohost.backup')
def backup_create(name=None, description=None, ignore_apps=False): def backup_create(name=None, description=None, output_directory=None,
no_compress=False, ignore_apps=False):
""" """
Create a backup local archive Create a backup local archive
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
output_directory -- Output directory for the backup
no_compress -- Do not create an archive file
ignore_apps -- Do not backup apps ignore_apps -- Do not backup apps
""" """
# TODO: Add a 'clean' argument to clean output directory
from yunohost.hook import hook_add from yunohost.hook import hook_add
from yunohost.hook import hook_callback from yunohost.hook import hook_callback
tmp_dir = None
# Validate and define backup name
timestamp = int(time.time()) timestamp = int(time.time())
if not name: if not name:
name = str(timestamp) name = str(timestamp)
if name in backup_list()['archives']: if name in backup_list()['archives']:
raise MoulinetteError(errno.EINVAL, m18n.n('backup_archive_name_exists')) raise MoulinetteError(errno.EINVAL,
tmp_dir = "%s/tmp/%s" % (backup_path, name) m18n.n('backup_archive_name_exists'))
# Initialize backup info # Validate additional arguments
info = { if no_compress and not output_directory:
'description': description or '', raise MoulinetteError(errno.EINVAL,
'created_at': timestamp, m18n.n('backup_output_directory_required'))
'apps': {}, if output_directory:
} output_directory = os.path.abspath(output_directory)
# Check for forbidden folders
if output_directory.startswith(archives_path) or \
re.match(r'^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$',
output_directory):
logger.error("forbidden output directory '%'", output_directory)
raise MoulinetteError(errno.EINVAL,
m18n.n('backup_output_directory_forbidden'))
# Create the output directory
if not os.path.isdir(output_directory):
logger.info("creating output directory '%s'", output_directory)
os.makedirs(output_directory, 0750)
# Check that output directory is empty
elif no_compress and os.listdir(output_directory):
logger.error("not empty output directory '%'", output_directory)
raise MoulinetteError(errno.EIO,
m18n.n('backup_output_directory_not_empty'))
# Define temporary directory
if no_compress:
tmp_dir = output_directory
else:
output_directory = archives_path
# Create temporary directory # Create temporary directory
if not tmp_dir:
tmp_dir = "%s/tmp/%s" % (backup_path, name)
if os.path.isdir(tmp_dir): if os.path.isdir(tmp_dir):
logger.warning("temporary directory for backup '%s' already exists", tmp_dir) logger.warning("temporary directory for backup '%s' already exists",
tmp_dir)
os.system('rm -rf %s' % tmp_dir) os.system('rm -rf %s' % tmp_dir)
try: try:
os.mkdir(tmp_dir, 0750) os.mkdir(tmp_dir, 0750)
@ -80,12 +115,19 @@ def backup_create(name=None, description=None, ignore_apps=False):
else: else:
os.system('chown -hR admin: %s' % tmp_dir) os.system('chown -hR admin: %s' % tmp_dir)
# Initialize backup info
info = {
'description': description or '',
'created_at': timestamp,
'apps': {},
}
# Add apps backup hook # Add apps backup hook
if not ignore_apps: if not ignore_apps:
from yunohost.app import app_info from yunohost.app import app_info
try: try:
for app_id in os.listdir('/etc/yunohost/apps'): for app_id in os.listdir('/etc/yunohost/apps'):
hook = '/etc/yunohost/apps/'+ app_id +'/scripts/backup' hook = '/etc/yunohost/apps/%s/scripts/backup' % app_id
if os.path.isfile(hook): if os.path.isfile(hook):
hook_add(app_id, hook) hook_add(app_id, hook)
@ -95,7 +137,8 @@ def backup_create(name=None, description=None, ignore_apps=False):
'version': i['version'], 'version': i['version'],
} }
else: else:
logger.warning("unable to find app's backup hook '%s'", hook) logger.warning("unable to find app's backup hook '%s'",
hook)
msignals.display(m18n.n('unbackup_app', app_id), msignals.display(m18n.n('unbackup_app', app_id),
'warning') 'warning')
except IOError as e: except IOError as e:
@ -110,8 +153,9 @@ def backup_create(name=None, description=None, ignore_apps=False):
f.write(json.dumps(info)) f.write(json.dumps(info))
# Create the archive # Create the archive
if not no_compress:
msignals.display(m18n.n('backup_creating_archive')) msignals.display(m18n.n('backup_creating_archive'))
archive_file = "%s/%s.tar.gz" % (archives_path, name) archive_file = "%s/%s.tar.gz" % (output_directory, name)
try: try:
tar = tarfile.open(archive_file, "w:gz") tar = tarfile.open(archive_file, "w:gz")
except: except:
@ -123,7 +167,7 @@ def backup_create(name=None, description=None, ignore_apps=False):
try: try:
tar = tarfile.open(archive_file, "w:gz") tar = tarfile.open(archive_file, "w:gz")
except: except:
logger.exception("unable to open the archive '%s' for writing " \ logger.exception("unable to open the archive '%s' for writing "
"after creating directory '%s'", "after creating directory '%s'",
archive_file, archives_path) archive_file, archives_path)
tar = None tar = None
@ -131,12 +175,17 @@ def backup_create(name=None, description=None, ignore_apps=False):
logger.exception("unable to open the archive '%s' for writing", logger.exception("unable to open the archive '%s' for writing",
archive_file) archive_file)
if tar is None: if tar is None:
raise MoulinetteError(errno.EIO, m18n.n('backup_archive_open_failed')) raise MoulinetteError(errno.EIO,
m18n.n('backup_archive_open_failed'))
tar.add(tmp_dir, arcname='') tar.add(tmp_dir, arcname='')
tar.close() tar.close()
# Copy info file and remove temporary directory # Copy info file
os.system('mv %s/info.json %s/%s.info.json' % (tmp_dir, archives_path, name)) os.system('mv %s/info.json %s/%s.info.json' %
(tmp_dir, archives_path, name))
# Clean temporary directory
if tmp_dir != output_directory:
os.system('rm -rf %s' % tmp_dir) os.system('rm -rf %s' % tmp_dir)
msignals.display(m18n.n('backup_complete'), 'success') msignals.display(m18n.n('backup_complete'), 'success')
@ -260,6 +309,7 @@ def backup_list():
return { 'archives': result } return { 'archives': result }
def backup_info(name): def backup_info(name):
""" """
Get info about a local backup archive Get info about a local backup archive

View file

@ -128,6 +128,9 @@
"packages_upgrade_failed" : "Unable to upgrade all packages", "packages_upgrade_failed" : "Unable to upgrade all packages",
"system_upgraded" : "System successfully upgraded", "system_upgraded" : "System successfully upgraded",
"backup_output_directory_required" : "You must provide an output directory for the backup",
"backup_output_directory_forbidden" : "Forbidden output directory",
"backup_output_directory_not_empty" : "Output directory is not empty",
"backup_running_hooks" : "Running backup hooks...", "backup_running_hooks" : "Running backup hooks...",
"backup_creating_archive" : "Creating the backup archive...", "backup_creating_archive" : "Creating the backup archive...",
"backup_extracting_archive" : "Extracting the backup archive...", "backup_extracting_archive" : "Extracting the backup archive...",

View file

@ -128,6 +128,9 @@
"packages_upgrade_failed" : "Impossible de mettre à jour tous les paquets", "packages_upgrade_failed" : "Impossible de mettre à jour tous les paquets",
"system_upgraded" : "Système mis à jour avec succès", "system_upgraded" : "Système mis à jour avec succès",
"backup_output_directory_required" : "Vous devez spécifier un dossier de sortie pour la sauvegarde",
"backup_output_directory_forbidden" : "Dossier de sortie interdit",
"backup_output_directory_not_empty" : "Le dossier de sortie n'est pas vide",
"backup_running_hooks" : "Exécution des scripts de sauvegarde...", "backup_running_hooks" : "Exécution des scripts de sauvegarde...",
"backup_creating_archive" : "Création de l'archive de sauvegarde...", "backup_creating_archive" : "Création de l'archive de sauvegarde...",
"backup_extracting_archive" : "Extraction de l'archive de sauvegarde...", "backup_extracting_archive" : "Extraction de l'archive de sauvegarde...",