Skip to content

Commit

Permalink
Merge branch 'master' into func_test_dbbackup
Browse files Browse the repository at this point in the history
  • Loading branch information
ZuluPro committed Jul 27, 2015
2 parents c092540 + 878e3e0 commit 704c057
Show file tree
Hide file tree
Showing 16 changed files with 158 additions and 141 deletions.
57 changes: 0 additions & 57 deletions 2to3.patch

This file was deleted.

26 changes: 22 additions & 4 deletions README.rst
Expand Up @@ -8,19 +8,37 @@ Django Database Backup
:target: https://readthedocs.org/projects/django-dbbackup/?badge=latest
:alt: Documentation Status


.. image:: https://coveralls.io/repos/django-dbbackup/django-dbbackup/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/django-dbbackup/django-dbbackup?branch=master


This Django application provides management commands to help backup and
restore your project database to AmazonS3, Dropbox or Local Disk.
restore your project database and media files with various storages such as
Amazon S3, DropBox or local file storage.

It is made for:

- Keep your important data secure and offsite.
- Ensure yours backup with GPG signature and encryption
- Archive with compression
- Use Crontab or Celery to setup automated backups.
- Great to keep your development database up to date.

Docs
====

http://django-dbbackup.readthedocs.org/
See our offical documentation at `Read The Docs`_.

Contributing
============

All contribution are very welcomed, propositions, problems, bugs and
enhancement are tracked with `GitHub issues`_ system and patch are submitted
via `pull requests`_.

We use `Travis`_ coupled with `Coveralls`_ as continious integration tools.

.. _`Read The Docs`: http://django-dbbackup.readthedocs.org/
.. _`GitHub issues`: https://github.com/django-dbbackup/django-dbbackup/issues
.. _`pull requests`: https://github.com/django-dbbackup/django-dbbackup/pulls
.. _Travis: https://travis-ci.org/django-dbbackup/django-dbbackup
.. _Coveralls: https://coveralls.io/github/django-dbbackup/django-dbbackup
13 changes: 0 additions & 13 deletions TODO.md

This file was deleted.

7 changes: 6 additions & 1 deletion dbbackup/__init__.py
@@ -1 +1,6 @@
VERSION = "2.0.5a"
"Management commands to help backup and restore a project database to \
AmazonS3, Dropbox or local disk."
VERSION = __version__ = "2.0.5a"
__author__ = 'Michael Shepanski'
__email__ = 'mjs7231@gmail.com'
__url__ = 'https://github.com/django-dbbackup/django-dbbackup'
18 changes: 18 additions & 0 deletions dbbackup/management/commands/_base.py
@@ -0,0 +1,18 @@
from optparse import make_option
from django.core.management.base import BaseCommand, LabelCommand


class BaseDbBackupCommand(LabelCommand):
option_list = BaseCommand.option_list + (
make_option("--noinput", action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.'),
make_option('-q', "--quiet", action='store_true', default=False,
help='Tells Django to NOT output other text than errors.')
)

verbosity = 1
quiet = False

def log(self, msg, level):
if not self.quiet and self.verbosity >= level:
self.stdout.write(msg)
29 changes: 16 additions & 13 deletions dbbackup/management/commands/dbbackup.py
@@ -1,5 +1,5 @@
"""
Save backup files to Dropbox.
Save database.
"""
from __future__ import (absolute_import, division,
print_function, unicode_literals)
Expand All @@ -9,22 +9,22 @@
import tempfile
import gzip
from shutil import copyfileobj

from django.conf import settings
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError
from django.core.management.base import LabelCommand
from optparse import make_option

from dbbackup.management.commands._base import BaseDbBackupCommand
from dbbackup import utils
from dbbackup.dbcommands import DBCommands
from dbbackup.storage.base import BaseStorage
from dbbackup.storage.base import StorageError
from dbbackup import settings as dbbackup_settings


class Command(LabelCommand):
class Command(BaseDbBackupCommand):
help = "dbbackup [-c] [-d <dbname>] [-s <servername>] [--compress] [--encrypt]"
option_list = BaseCommand.option_list + (
option_list = BaseDbBackupCommand.option_list + (
make_option("-c", "--clean", help="Clean up old backup files", action="store_true", default=False),
make_option("-d", "--database", help="Database to backup (default: everything)"),
make_option("-s", "--servername", help="Specify server name to include in backup filename"),
Expand All @@ -35,9 +35,11 @@ class Command(LabelCommand):
@utils.email_uncaught_exception
def handle(self, **options):
""" Django command handler. """
self.verbosity = int(options.get('verbosity'))
self.quiet = options.get('quiet')
try:
self.clean = options.get('clean')
self.clean_keep = getattr(settings, 'DBBACKUP_CLEANUP_KEEP', 10)
self.clean_keep = dbbackup_settings.CLEANUP_KEEP
self.database = options.get('database')
self.servername = options.get('servername')
self.compress = options.get('compress')
Expand All @@ -50,44 +52,45 @@ def handle(self, **options):
for database_key in database_keys:
database = settings.DATABASES[database_key]
self.dbcommands = DBCommands(database)
self.save_new_backup(database, database_key)
self.save_new_backup(database)
self.cleanup_old_backups(database)
except StorageError as err:
raise CommandError(err)

def save_new_backup(self, database, database_name):
def save_new_backup(self, database):
""" Save a new backup file. """
print("Backing Up Database: %s" % database['NAME'])
self.log("Backing Up Database: %s" % database['NAME'], 1)
filename = self.dbcommands.filename(self.servername)
outputfile = tempfile.SpooledTemporaryFile(
max_size=10 * 1024 * 1024,
dir=dbbackup_settings.TMP_DIR)
self.dbcommands.run_backup_commands(outputfile)
outputfile.name = filename
if self.compress:
compressed_file, filename = self.compress_file(outputfile, filename)
outputfile.close()
outputfile = compressed_file
if self.encrypt:
encrypted_file, filename = utils.encrypt_file(outputfile, filename)
outputfile = encrypted_file
print(" Backup tempfile created: %s" % (utils.handle_size(outputfile)))
print(" Writing file to %s: %s, filename: %s" % (self.storage.name, self.storage.backup_dir, filename))
self.log(" Backup tempfile created: %s" % (utils.handle_size(outputfile)), 1)
self.log(" Writing file to %s: %s, filename: %s" % (self.storage.name, self.storage.backup_dir, filename), 1)
self.storage.write_file(outputfile, filename)

def cleanup_old_backups(self, database):
""" Cleanup old backups, keeping the number of backups specified by
DBBACKUP_CLEANUP_KEEP and any backups that occur on first of the month.
"""
if self.clean:
print("Cleaning Old Backups for: %s" % database['NAME'])
self.log("Cleaning Old Backups for: %s" % database['NAME'], 1)
filepaths = self.storage.list_directory()
filepaths = self.dbcommands.filter_filepaths(filepaths)
for filepath in sorted(filepaths[0:-self.clean_keep]):
regex = r'^%s' % self.dbcommands.filename_match(self.servername, '(.*?)')
datestr = re.findall(regex, os.path.basename(filepath))[0]
dateTime = datetime.datetime.strptime(datestr, dbbackup_settings.DATE_FORMAT)
if int(dateTime.strftime("%d")) != 1:
print(" Deleting: %s" % filepath)
self.log(" Deleting: %s" % filepath, 1)
self.storage.delete_file(filepath)

def compress_file(self, inputfile, filename):
Expand Down
44 changes: 24 additions & 20 deletions dbbackup/management/commands/dbrestore.py
@@ -1,51 +1,55 @@
"""
Restore pgdump files from Dropbox.
See __init__.py for a list of options.
Restore database.
"""
from __future__ import (absolute_import, division,
print_function, unicode_literals)
import os
import tempfile
import gzip
import sys
from getpass import getpass

from ... import utils
from ...dbcommands import DBCommands
from ...storage.base import BaseStorage
from ...storage.base import StorageError
from dbbackup import settings as dbbackup_settings
from django.conf import settings
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError
from django.core.management.base import LabelCommand
from django.utils import six
from django.db import connection

from optparse import make_option

from dbbackup.management.commands._base import BaseDbBackupCommand
from dbbackup import utils
from dbbackup.dbcommands import DBCommands
from dbbackup.storage.base import BaseStorage, StorageError
from dbbackup import settings as dbbackup_settings

input = raw_input if six.PY2 else input # @ReservedAssignment


class Command(LabelCommand):
class Command(BaseDbBackupCommand):
help = "dbrestore [-d <dbname>] [-f <filename>] [-s <servername>]"
option_list = BaseCommand.option_list + (
option_list = BaseDbBackupCommand.option_list + (
make_option("-d", "--database", help="Database to restore"),
make_option("-f", "--filepath", help="Specific file to backup from"),
make_option("-x", "--backup-extension", help="The extension to use when scanning for files to restore from."),
make_option("-s", "--servername", help="Use a different servername backup"),
make_option("-l", "--list", action='store_true', default=False, help="List backups in the backup directory"),
make_option("-c", "--decrypt", help="Decrypt data before restoring", default=False, action='store_true'),
make_option("-p", "--passphrase", help="Passphrase for decrypt file", default=None),
make_option("-z", "--uncompress", help="Uncompress gzip data before restoring", action='store_true'),
)

def handle(self, **options):
""" Django command handler. """
self.verbosity = int(options.get('verbosity'))
self.quiet = options.get('quiet')
try:
connection.close()
self.filepath = options.get('filepath')
self.backup_extension = options.get('backup-extension') or 'backup'
self.backup_extension = options.get('backup_extension') or 'backup'
self.servername = options.get('servername')
self.decrypt = options.get('decrypt')
self.uncompress = options.get('uncompress')
self.passphrase = options.get('passphrase')
self.database = self._get_database(options)
self.storage = BaseStorage.storage_factory()
self.dbcommands = DBCommands(self.database)
Expand All @@ -68,17 +72,17 @@ def _get_database(self, options):

def restore_backup(self):
""" Restore the specified database. """
self.stdout.write("Restoring backup for database: %s" % self.database['NAME'])
self.log("Restoring backup for database: %s" % self.database['NAME'], 1)
# Fetch the latest backup if filepath not specified
if not self.filepath:
self.stdout.write(" Finding latest backup")
self.log(" Finding latest backup", 1)
filepaths = self.storage.list_directory()
filepaths = [f for f in filepaths if f.endswith('.' + self.backup_extension)]
if not filepaths:
raise CommandError("No backup files found in: /%s" % self.storage.backup_dir)
self.filepath = filepaths[-1]
# Restore the specified filepath backup
self.stdout.write(" Restoring: %s" % self.filepath)
self.log(" Restoring: %s" % self.filepath, 1)
input_filename = self.filepath
inputfile = self.storage.read_file(input_filename)
if self.decrypt:
Expand All @@ -89,10 +93,10 @@ def restore_backup(self):
uncompressed_file = self.uncompress_file(inputfile)
inputfile.close()
inputfile = uncompressed_file
self.stdout.write(" Restore tempfile created: %s" % utils.handle_size(inputfile))
self.log(" Restore tempfile created: %s" % utils.handle_size(inputfile), 1)
answer = input("Are you sure you want to continue? [Y/n]")
if answer.lower() not in ('y', 'yes', ''):
self.stdout.write("Quitting")
self.log("Quitting", 1)
sys.exit(0)
inputfile.seek(0)
self.dbcommands.run_restore_commands(inputfile)
Expand All @@ -119,7 +123,7 @@ def unencrypt_file(self, inputfile, inputfilename):
import gnupg

def get_passphrase():
return input('Input Passphrase: ')
return self.passphrase or getpass('Input Passphrase: ') or None

temp_dir = tempfile.mkdtemp(dir=dbbackup_settings.TMP_DIR)
try:
Expand Down Expand Up @@ -149,8 +153,8 @@ def get_passphrase():

def list_backups(self):
""" List backups in the backup directory. """
self.stdout.write("Listing backups on %s in /%s:" % (self.storage.name, self.storage.backup_dir))
self.log("Listing backups on %s in /%s:" % (self.storage.name, self.storage.backup_dir), 1)
for filepath in self.storage.list_directory():
self.stdout.write(" %s" % os.path.basename(filepath))
self.log(" %s" % os.path.basename(filepath), 1)
# TODO: Implement filename_details method
# print(utils.filename_details(filepath))

0 comments on commit 704c057

Please sign in to comment.