diff --git a/openslides/main.py b/openslides/main.py index 7f26b875a..6dc1757f1 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -89,6 +89,9 @@ def process_options(argv=None, manage_runserver=False): parser.add_option( "--syncdb", action="store_true", help="Update/create database before starting the server.") + parser.add_option( + "--backupdb", action="store", metavar="BACKUP_PATH", + help="Make a backup copy of the database to BACKUP_PATH") parser.add_option( "--reset-admin", action="store_true", help="Make sure the user 'admin' exists and uses 'admin' as password.") @@ -190,6 +193,9 @@ def _main(opts, database_path=None): elif opts.reset_admin: create_or_reset_admin_user() + if opts.backupdb: + backup_database(opts.backupdb) + if opts.no_run: return @@ -320,6 +326,11 @@ def create_or_reset_admin_user(): admin.save() +def backup_database(dest_path): + argv = ["", "backupdb", "--destination={0}".format(dest_path)] + execute_from_command_line(argv) + + def start_browser(url): browser = webbrowser.get() diff --git a/openslides/utils/management/commands/backupdb.py b/openslides/utils/management/commands/backupdb.py new file mode 100644 index 000000000..f3a374ebe --- /dev/null +++ b/openslides/utils/management/commands/backupdb.py @@ -0,0 +1,53 @@ +import shutil +from optparse import make_option + +import django.conf +import django.db +import django.db.transaction +from django.core.management.base import NoArgsCommand, CommandError + + +class Command(NoArgsCommand): + help = "Backup the openslides database" + option_list = NoArgsCommand.option_list + ( + make_option( + "--destination", action="store", + help="path to the backup database (will be overwritten)"), + ) + + def handle_noargs(self, *args, **kw): + db_settings = django.conf.settings.DATABASES + default = db_settings.get(django.db.DEFAULT_DB_ALIAS) + if not default: + raise CommandError("Default databases is not configured") + + if default.get("ENGINE") != "django.db.backends.sqlite3": + raise CommandError( + "Only sqlite3 databases can currently be backuped") + + src_path = default.get("NAME") + if not src_path: + raise CommandError("No path specified for default database") + + dest_path = kw.get("destination") + if not dest_path: + raise CommandError("--destination must be specified") + + self.do_backup(src_path, dest_path) + + @django.db.transaction.commit_manually + def do_backup(self, src_path, dest_path): + # perform a simple file-copy backup of the database + # first we need a shared lock on the database, issuing a select() + # will do this for us + cursor = django.db.connection.cursor() + cursor.execute("SELECT count(*) from sqlite_master") + + # now copy the file + try: + shutil.copy(src_path, dest_path) + except IOError as e: + raise CommandError("{0}\nDatabase backup failed!".format(e)) + + # and release the lock again + django.db.transaction.commit()