yunohost/backup.py

212 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
""" License
Copyright (C) 2013 YunoHost
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses
"""
""" yunohost_backup.py
Manage backups
"""
import os
import sys
import json
import errno
import time
import shutil
import tarfile
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
backup_path = '/home/yunohost.backup'
archives_path = '%s/archives' % backup_path
logger = getActionLogger('yunohost.backup')
def backup_create(ignore_apps=False):
"""
Backup and create a local archive
Keyword arguments:
ignore_apps -- Do not backup apps
"""
from yunohost.hook import hook_add
from yunohost.hook import hook_callback
timestamp = int(time.time())
tmp_dir = "%s/tmp/%s" % (backup_path, timestamp)
# Create temporary directory
if os.path.isdir(tmp_dir):
logger.warning("temporary directory for backup '%s' already exists", tmp_dir)
os.system('rm -rf %s' % tmp_dir)
try:
os.mkdir(tmp_dir, 0750)
except OSError:
# Create temporary directory recursively
os.makedirs(tmp_dir, 0750)
os.system('chown -hR admin: %s' % backup_path)
else:
os.system('chown -hR admin: %s' % tmp_dir)
# Add app's backup hooks
if not ignore_apps:
try:
for app_id in os.listdir('/etc/yunohost/apps'):
hook = '/etc/yunohost/apps/'+ app_id +'/scripts/backup'
if os.path.isfile(hook):
hook_add(app_id, hook)
else:
logger.warning("unable to find app's backup hook '%s'", hook)
msignals.display(m18n.n('unbackup_app', app_id),
'warning')
except IOError as e:
logger.info("unable to add app's backup hooks: %s", str(e))
# Run hooks
m18n.display(m18n.n('backup_running_hooks'))
hook_callback('backup', [tmp_dir])
# TODO: Add a backup info file
# Create the archive
m18n.display(m18n.n('backup_creating_archive'))
archive_file = "%s/%s.tar.gz" % (archives_path, timestamp)
try:
tar = tarfile.open(archive_file, "w:gz")
except:
tar = None
# Create the archives directory and retry
if not os.path.isdir(archives_path):
os.mkdir(archives_path, 0750)
try:
tar = tarfile.open(archive_file, "w:gz")
except:
logger.exception("unable to open the archive '%s' for writing " \
"after creating directory '%s'",
archive_file, archive_dir)
tar = None
else:
logger.exception("unable to open the archive '%s' for writing",
archive_file)
if tar is None:
raise MoulinetteError(errno.EIO, m18n.n('backup_archive_open_failed'))
tar.add(tmp_dir, arcname='')
tar.close()
# Remove temporary directory
os.system('rm -rf %s' % tmp_dir)
msignals.display(m18n.n('backup_complete'), 'success')
def backup_restore(path):
"""
Restore from an encrypted backup tarball
Keyword argument:
path -- Path to the restore directory
"""
from yunohost.tools import tools_postinstall
from yunohost.hook import hook_add
from yunohost.hook import hook_callback
path = os.path.abspath(path)
try:
with open("%s/yunohost/current_host" % path, 'r') as f:
domain = f.readline().rstrip()
except IOError:
raise MoulinetteError(errno.EINVAL, m18n.n('invalid_restore_package'))
#TODO Decrypt & extract tarball
try:
with open('/etc/yunohost/installed') as f:
#raise MoulinetteError(errno.EINVAL, m18n.n('yunohost_already_installed'))
msignals.display(m18n.n('restoring_installed_system'), 'warning')
time.sleep(5)
pass
except IOError:
tools_postinstall(domain, 'yunohost', True)
# Add app's restore hooks
try:
for app_id in os.listdir('/etc/yunohost/apps'):
hook = '/etc/yunohost/apps/'+ app_id +'/scripts/restore'
if os.path.isfile(hook):
hook_add(app_id, hook)
else:
msignals.display(m18n.n('unrestore_app', app_id),
'warning')
except IOError:
pass
# Run hook
hook_callback('restore', [path])
msignals.display(m18n.n('restore_complete'), 'success')
def backup_list():
"""
List available local backup archives
"""
result = []
try:
# Retrieve local archives
archives = os.listdir(archives_path)
except IOError as e:
logging.info("unable to iterate over local archives: %s", str(e))
else:
# Iterate over local archives
for f in archives:
try:
name = f[:f.rindex('.tar.gz')]
except ValueError:
continue
result.append(name)
return { 'archives': result }
def backup_info(name):
"""
Get info about a local backup archive
Keyword arguments:
name -- Name of the local backup archive
"""
archive_file = '%s/%s.tar.gz' % (archives_path, name)
if not os.path.isfile(archive_file):
logger.error("no local backup archive found at '%s'", archive_file)
raise MoulinetteError(errno.EIO, m18n.n('backup_archive_name_unknown'))
return {
'path': archive_file,
# TODO: Retrieve created_at from the info file
'created_at': time.strftime(m18n.n('format_datetime_short'),
time.gmtime(int(name))),
}