FabricΒΆ
Sample tasks:
@task
def db_backup():
"""
Backup the database to S3 just like the nightly cron job
"""
require('environment')
require('instance', provided_by='instance')
manage_run("dbbackup --encrypt")
def db_exists(dbname):
"""
Return True if a db named DBNAME exists on the remote host.
"""
require('environment', provided_by=SERVER_ENVIRONMENTS)
output = sudo('psql -l --pset=format=unaligned', user='postgres')
dbnames = [line.split('|')[0] for line in output.splitlines()]
return dbname in dbnames
@task
def db_dump(file):
"""
Dump an instance's database to a remote file.
Example:
`fab staging instance:iraq db_dump:/tmp/staging_iraq.dump`
dumps to staging_iraq.dump
"""
require('environment', provided_by=SERVER_ENVIRONMENTS)
require('instance', provided_by='instance')
remote_file = file
if files.exists(file):
if not confirm("Remote file {file} exists and will be overwritten. Okay?"
.format(file=remote_file)):
abort("ERROR: aborting")
# Don't need remote DB user and password because we're going to run pg_dump as user postgres
sudo('pg_dump --format=custom --file={outputfile} {dbname}'
.format(dbname=env.db_name, outputfile=remote_file),
user='postgres')
print("Database from {environment} {instance} has been dumped to remote file {file}"
.format(environment=env.environment, instance=env.instance, file=remote_file))
@task
def local_restore(file):
"""
Restore a local dump file to the local instance's database.
:param file:
:return:
"""
# Find out the local DB settings
import sys
sys.path[0:0] = ['.']
from cts.settings.local import DATABASES
DB = DATABASES['default']
assert DB['ENGINE'] == 'django.contrib.gis.db.backends.postgis'
dbname = DB['NAME']
owner = DB['USER'] or os.getenv('USER')
local('dropdb {dbname} || true'.format(dbname=dbname), shell="/bin/sh")
local('createdb --encoding UTF8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8 --template=template0 --owner {owner} {dbname}'.format(owner=owner, dbname=dbname))
local('sudo -u postgres pg_restore -Ox -j4 --dbname={dbname} {file}'.format(dbname=dbname, file=file))
@task
def db_restore(file):
"""
Restore a remote DB dump file to a remote instance's database.
This will rename the existing database to {previous_name}_bak
and create a completely new database with what's in the dump.
If there's already a backup database, the restore will fail.
Example:
`fab staging instance:iraq db_restore:/tmp/staging_iraq.dump`
:param file: The remote file to restore.
"""
require('environment', provided_by=SERVER_ENVIRONMENTS)
require('instance', provided_by='instance')
renamed = False
restored = False
if not files.exists(file):
abort("Remote file {file} does not exist".format(file=file))
try:
if db_exists(env.db_name):
# Rename existing DB to backup
db_backup = '{dbname}_bak'.format(dbname=env.db_name)
if db_exists(db_backup):
if confirm("There's already a database named {db_backup}. Replace with new backup?"
.format(db_backup=db_backup)):
sudo('dropdb {db_backup}'.format(db_backup=db_backup),
user='postgres')
else:
abort("ERROR: There's already a database named {db_backup}. "
"Restoring would clobber it."
.format(db_backup=db_backup))
sudo('psql -c "ALTER DATABASE {dbname} RENAME TO {db_backup}"'
.format(dbname=env.db_name, db_backup=db_backup),
user='postgres')
renamed = True
print("Renamed {dbname} to {db_backup}".format(dbname=env.db_name, db_backup=db_backup))
remote_file = file
# Create new, very empty database.
# * We can't use --create on the pg_restore because that will always restore to whatever
# db name was saved in the dump file, and we don't want to be restricted that way.
# * Any extensions the backed-up database had will be included in the restore, so we
# don't need to enable them now.
# If these parameters change, also change the parameters in conf/salt/project/db/init.sls
# (TODO: we could use the output of psql -l to copy most of these settings from the
# existing database.)
sudo('createdb --encoding UTF8 --lc-collate=en_US.UTF-8 '
'--lc-ctype=en_US.UTF-8 --template=template0 --owner {owner} {dbname}'
.format(dbname=env.db_name, owner=env.db_owner),
user='postgres')
# Don't need remote DB user and password because we're going to
# run pg_restore as user postgres
sudo('pg_restore -1 --dbname={dbname} {filename}'
.format(dbname=env.db_name, filename=remote_file),
user='postgres')
restored = True
# Run ANALYZE on the db to help Postgres optimize how it accesses it
sudo('psql {dbname} -c ANALYZE'.format(dbname=env.db_name),
user='postgres')
print("Database for {environment} {instance} has been restored from remote file {file}"
.format(environment=env.environment, instance=env.instance, file=remote_file))
finally:
if renamed and not restored:
print("Error occurred after renaming current database, trying to rename it back")
if db_exists(env.db_name):
# We already created the new db, but restore failed; delete it
sudo('dropdb {dbname}'.format(dbname=env.dbname), user='postgres')
sudo('psql -c "ALTER DATABASE {db_backup} RENAME TO {dbname}"'
.format(dbname=env.db_name, db_backup=db_backup),
user='postgres')
print("Successfully put back the original database.")