import os
import pkg_resources
import copy
+import decorator
-from wizard import shell
+import wizard
+from wizard import plugin, shell
# We're going to use sqlalchemy.engine.url.URL as our database
# info intermediate object
url = sqlalchemy.engine.url.make_url(env_dsn)
url.database = old_url.database
return url
+
+def backup(outdir, deployment):
+ """
+ Generic database backup function.
+ """
+ # XXX: Change this once deployments support multiple dbs
+ if deployment.application.database == "mysql":
+ return backup_mysql(outdir, deployment)
+ else:
+ raise NotImplementedError
+
+def backup_mysql(outdir, deployment):
+ """
+ Database backups for MySQL using the :command:`mysqldump` utility.
+ """
+ outfile = os.path.join(outdir, "db.sql")
+ try:
+ shell.call("mysqldump", "--compress", "-r", outfile, *get_mysql_args(deployment.dsn))
+ shell.call("gzip", "--best", outfile)
+ except shell.CallError as e:
+ raise BackupDatabaseError(e.stderr)
+
+def restore(backup_dir, deployment):
+ """
+ Generic database restoration function.
+ """
+ # XXX: see backup
+ if deployment.application.database == "mysql":
+ return restore_mysql(backup_dir, deployment)
+ else:
+ raise NotImplementedError
+
+def restore_mysql(backup_dir, deployment):
+ """
+ Database restoration for MySQL by piping SQL commands into :command:`mysql`.
+ """
+ if not os.path.exists(backup_dir):
+ raise RestoreDatabaseError("Backup %s doesn't exist" % backup_dir.rpartition("/")[2])
+ sql = open(os.path.join(backup_dir, "db.sql"), 'w+')
+ shell.call("gunzip", "-c", os.path.join(backup_dir, "db.sql.gz"), stdout=sql)
+ sql.seek(0)
+ shell.call("mysql", *get_mysql_args(deployment.dsn), stdin=sql)
+ sql.close()
+
+def drop(url):
+ """
+ Generic drop database function. Attempts to run ``DROP
+ DATABASE`` on the database if no plugins succeed.
+
+ This function implements the plugin interface named
+ :ref:`wizard.sql.drop`.
+ """
+ r = plugin.hook("wizard.sql.drop", [url])
+ if r is not None:
+ return
+ engine = sqlalchemy.create_engine(url)
+ engine.execute("DROP DATABASE `%s`" % url.database)
+
+def get_mysql_args(dsn):
+ """
+ Extracts arguments that would be passed to the command line mysql utility
+ from a deployment.
+ """
+ args = []
+ if dsn.host:
+ args += ["-h", dsn.host]
+ if dsn.username:
+ args += ["-u", dsn.username]
+ if dsn.password:
+ args += ["-p" + dsn.password]
+ args += [dsn.database]
+ return args
+
+class Error(wizard.Error):
+ """Generic error class for this module."""
+ pass
+
+class BackupDatabaseError(Error):
+ """Backup script failed."""
+ #: String details of failure
+ details = None
+ def __init__(self, details):
+ self.details = details
+ def __str__(self):
+ return """
+
+ERROR: Backing up the database failed, details:
+
+%s""" % self.details
+
+class RestoreDatabaseError(Error):
+ """Restore script failed."""
+ #: String details of failure
+ details = None
+ def __init__(self, details):
+ self.details = details
+ def __str__(self):
+ return """
+
+ERROR: Restoring the database failed, details:
+
+%s""" % self.details
+
+class RemoveDatabaseError(Error):
+ """Removing the database failed."""
+ #: String details of failure
+ details = None
+ def __init__(self, details):
+ self.details = details
+ def __str__(self):
+ return """
+
+ERROR: Removing the database failed, details:
+
+%s""" % self.details