Permalink
Browse files

Added mysql support command mostly for dumping the database.

Added rackspace support for uploading to and listing rackspace containers.
Added backup command to integrate mysql dumping and rackspace uploading.
  • Loading branch information...
1 parent 1d2af9e commit 5a9ddea78d4f6dbe44620e18b19c3f93054522d2 @dpetzold committed Feb 23, 2012
@@ -0,0 +1,141 @@
+from __future__ import print_function
+
+from optparse import make_option
+
+import datetime
+import re
+import socket
+import subprocess
+import sys
+import os
+
+import django
+from django.core.management.base import CommandError, BaseCommand
+from django.conf import settings
+
+from django_extensions.management.commands.mysql import MysqlCommand
+from django_extensions.management.commands.rackspace import RackspaceCommand
+
+class BackupCommand(object):
+
+ def __init__(self, router, container_name, verbose=False):
+ self.mysql = MysqlCommand(router, verbose)
+ self.rackspace = RackspaceCommand(container_name, verbose)
+ self.verbose = verbose
+ self.start_time = datetime.datetime.now()
+
+ def _run_cmd(self, cmd, filepath, ext=None):
+ if self.verbose:
+ print(cmd)
+ subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
+ if ext is not None:
+ filepath += ext
+ filesize = os.stat(filepath).st_size
+ if filesize == 0:
+ raise baku_exception.BadFileSize('Bad filesize for "%s"' % (filepath))
+ return filepath, filesize
+
+ def tar_directory(self, directory, prefix=None):
+ root, name = os.path.split(directory)
+ name = '%s.%s-%s.tar.bz2' % \
+ (name, self.start_time.strftime('%Y%m%d_%H%M%S'), socket.gethostname())
+ if prefix is not None:
+ backup_dir = '%s/%s' % (settings.BACKUP_DIR, prefix)
+ else:
+ backup_dir = settings.BACKUP_DIR
+ if not os.path.exists(backup_dir):
+ os.mkdir(backup_dir)
+ filepath = '%s/%s' % (backup_dir, name)
+ cmd = '/bin/tar cfj %s %s -C %s' % (filepath, directory, root)
+ return self._run_cmd(cmd, filepath)
+
+ def cull(self):
+ culled = []
+ files = os.listdir(settings.BACKUP_DIR)
+ for filename in files:
+ for date in self.rackspace.culls:
+ if self.verbose:
+ print('Checking %s %s' % (date, filename))
+ search = re.search('\.%s_' % (date), filename)
+ if search is not None:
+ filepath = '%s/%s/' % (settings.BACKUP_DIR, filename)
+ if self.verbose:
+ print('Deleting %s' % (filepath))
+ if not self.dry_run:
+ os.unlink(filepath)
+ culled.append(filepath)
+ return culled
+
+ def backup_database(self):
+ dbfile = self.mysql.dump()
+ uploaded = self.rackspace.upload(dbfile, 'db')
+ if self.verbose:
+ print(uploaded.name)
+ return uploaded
+
+ def backup_site(self):
+ filepath, filesize = self.tar_directory(settings.SITE_ROOT, 'site')
+ if self.verbose:
+ print('%s %s' % (filepath, filesize))
+ uploaded = self.rackspace.upload(filepath, 'site')
+ if self.verbose:
+ print(uploaded.name)
+ return uploaded
+
+ def backup_all(self):
+ db_upload = self.backup_database()
+ site_upload = self.backup_site()
+ deletes = self.rackspace.cull()
+ if self.verbose:
+ for deleted in deletes:
+ print('Deleted: %s' % (deleted.name))
+ deletes = self.cull()
+ if self.verbose:
+ for deleted in deletes:
+ print('Deleted: %s' % (deleted.name))
+
+class Command(BaseCommand):
+
+ option_list = BaseCommand.option_list + (
+ make_option('-D', '--db', action='store_true',
+ dest='db', default=False,
+ help='Backup the database.'),
+ make_option('-C', '--contianer', action='store',
+ dest='container',
+ default=settings.RACKSPACE_BACKUP_CONTAINER,
+ help='The container to use.'),
+ make_option('--cull', action='store_true',
+ dest='cull', default=False,
+ help='Cull the backup files.'),
+ make_option('-R', '--router', action='store',
+ dest='router', default='default',
+ help='Use this router and not the default.'),
+ make_option('-S', '--site', action='store_true',
+ dest='site', default=False,
+ help='Backup the site directory.'),
+ make_option('-U', '--upload', action='store',
+ dest='upload', help='Upload the file to rackspace.'),
+ make_option('-V', '--verbose', action='store_true',
+ dest='verbose', default=False,
+ help='Be verbose.'),
+ )
+ help = """Backups the database and the site directory."""
+
+ requires_model_validation = False
+ can_import_settings = True
+
+ def handle(self, *args, **options):
+
+ backup = BackupCommand(
+ options.get('router'),
+ options.get('container'),
+ options.get('verbose'))
+
+ if options.get('cull'):
+ backup = backup.cull()
+ elif options.get('db'):
+ backup = backup.backup_database()
+ elif options.get('site'):
+ backup = backup.backup_site()
+ else:
+ backup.backup_all()
@@ -0,0 +1,97 @@
+from __future__ import print_function
+
+from optparse import make_option
+
+import datetime
+import socket
+import sys
+import os
+
+import django
+from django.core.management.base import CommandError, BaseCommand
+from django.conf import settings
+
+class MysqlCommand(object):
+
+ def __init__(self, router, verbose=False):
+
+ self.db = settings.DATABASES[router]
+ self.verbose = verbose
+
+ cmd = '%s '
+ if self.db['HOST'] != '':
+ cmd += '-h %s' % (self.db['HOST'])
+ cmd += '-p %s' % (self.db['PORT'])
+ cmd += '-u {user} --password={password}'.format(
+ user=self.db['USER'],
+ password=self.db['PASSWORD'])
+ self.mysqlbase = cmd
+
+ @property
+ def mysql(self):
+ return self.mysqlbase % ('mysql')
+
+ @property
+ def mysqladmin(self):
+ return self.mysqlbase % ('mysqladmin')
+
+ @property
+ def mysqldump(self):
+ return self.mysqlbase % ('mysqldump')
+
+ def drop(self):
+ os.system('{mysqladmin} -f DROP {database}'.format(
+ mysqladmin=self.mysqladmin,
+ database=self.db['NAME']))
+
+ def create(self):
+ os.sytem('{mysqladmin} CREATE {database}'.format(
+ mysqladmin=self.mysqladmin,
+ database=self.db['NAME']))
+
+ def source(self):
+ os.system('{mysql} {database} < {db_dump}'.format(
+ mysql=self.mysql,
+ database=self['NAME'],
+ db_dump=db_dump))
+
+ def dump(self, prefix='db'):
+
+ backup_dir = '%s/%s' % (settings.BACKUP_DIR, prefix)
+ if not os.path.exists(backup_dir):
+ os.mkdir(backup_dir)
+ dbfile = '{backup_dir}/{database}.{date}-{hostname}.gz'.format(
+ backup_dir=backup_dir,
+ database=self.db['NAME'],
+ date=datetime.datetime.now().strftime('%Y%m%d_%H%M%S'),
+ hostname=socket.gethostname())
+ output = os.system('{mysqldump} {database} | gzip > {db_file}'.format(
+ mysqldump=self.mysqldump,
+ db_file=dbfile,
+ database=self.db['NAME']))
+ return dbfile
+
+class Command(BaseCommand):
+
+ option_list = BaseCommand.option_list + (
+ make_option('-D', '--dump', action='store_true',
+ dest='dump', default=False,
+ help='Dump the database to the backup dir.'),
+ make_option('-R', '--router', action='store',
+ dest='router', default='default',
+ help='Use this router-database other then defined in settings.py'),
+ make_option('-V', '--verbose', action='store_true',
+ dest='verbose', default=False,
+ help='Be verbose.'),
+ )
+ help = """Backups the database and the site directory."""
+
+ requires_model_validation = False
+ can_import_settings = True
+
+ def handle(self, *args, **options):
+
+ mysql = MysqlCommand(options.get('router'))
+ if options.get('dump'):
+ dbfile = mysql.dump()
+ print(dbfile)
@@ -0,0 +1,133 @@
+from __future__ import print_function
+
+from optparse import make_option
+
+import datetime
+import re
+import socket
+import sys
+import os
+
+import django
+from django.core.management.base import CommandError, BaseCommand
+from django.conf import settings
+
+import cloudfiles
+
+class RackspaceCommand(object):
+
+ def __init__(self, container_name, verbose, dry_run=False, forward=0):
+ self.container_name = container_name
+ self.verbose = verbose
+ self.dry_run = dry_run
+ self.rp_conn = cloudfiles.get_connection(
+ settings.RACKSPACE_USER,
+ settings.RACKSPACE_API_KEY)
+ self.start_time = datetime.datetime.now()
+
+ if forward == 0:
+ self.start_time = datetime.datetime.now()
+ else:
+ self.start_time = datetime.datetime.now() + datetime.timedelta(days=forward)
+
+ @property
+ def culls(self):
+ dates = []
+ lastweeks = self.start_time - datetime.timedelta(weeks=1)
+
+ # delete last weeks except mondays
+ if lastweeks.weekday() != 1 and lastweeks.day != 1:
+ dates.append(lastweeks.strftime('%Y%m%d'))
+
+ # keep 8 weeks of mondays
+ lastmonths = self.start_time - datetime.timedelta(weeks=8)
+ if lastmonths.weekday() == 1 and lastmonths.day != 1:
+ dates.append(lastmonths.strftime('%Y%m%d'))
+ return dates
+
+ def cull(self):
+ culled = []
+ container = self.rp_conn.get_container(self.container_name)
+ for obj in container.get_objects():
+ for date in self.culls:
+ if self.verbose:
+ print('Checking %s %s' % (date, obj.name))
+ search = re.search('\.%s_' % (date), obj.name)
+ if search is not None:
+ if self.verbose:
+ print('Deleting %s' % (obj.name))
+ if not self.dry_run:
+ container.delete_object(obj.name)
+ culled.append(obj)
+ return culled
+
+ def list(self):
+ container = self.rp_conn.get_container(self.container_name)
+ for obj in container.get_objects():
+ print('%s %s' % (obj.name, obj.size))
+
+ def upload(self, filepath, prefix=None):
+ root, filename = os.path.split(filepath)
+ container = self.rp_conn.get_container(self.container_name)
+ if prefix:
+ filename = '%s/%s' % (prefix, filename)
+ obj = container.create_object(filename)
+ obj.load_from_filename(filepath)
+ return obj
+
+ def delete(self, filename, prefix=None):
+ container = self.rp_conn.get_container(self.container_name)
+ return container.delete_object(filename)
+
+class Command(BaseCommand):
+
+ option_list = BaseCommand.option_list + (
+ make_option('--cull', action='store_true',
+ dest='cull', default=False,
+ help='Cull the remote backup files.'),
+ make_option('-C', '--contianer', action='store',
+ dest='container',
+ default=settings.RACKSPACE_BACKUP_CONTAINER,
+ help='The container to use.'),
+ make_option('-D', '--delete', action='store', dest='delete',
+ help='Delete the specified file from container.'),
+ make_option('-F', '--forward', action='store',
+ dest='forward', default=0,
+ help='Pretend to run the specified days forward.'),
+ make_option('-L', '--list', action='store_true',
+ dest='list', default=False,
+ help='List the backup dir.'),
+ make_option('-N', '--dry-run', action='store_true',
+ dest='dry_run', default=False,
+ help='Dry run.'),
+ make_option('-U', '--upload', action='store',
+ dest='upload', help='Upload the file to rackspace.'),
+ make_option('-V', '--verbose', action='store_true',
+ dest='verbose', default=False,
+ help='Be verbose.'),
+ )
+ help = """Backups the database and the site directory."""
+
+ requires_model_validation = False
+ can_import_settings = True
+
+ def handle(self, *args, **options):
+
+ rackspace = RackspaceCommand(
+ options.get('container'),
+ options.get('verbose'),
+ options.get('dry_run'),
+ int(options.get('forward')))
+
+ if options.get('cull'):
+ deletes = rackspace.cull()
+ for deleted in deletes:
+ print('Deleted: %s' % (deleted.name))
+ elif options.get('delete'):
+ rackspace.delete(options.get('delete'))
+ elif options.get('list'):
+ rackspace.list()
+ elif options.get('upload'):
+ backup = rackspace.upload(options.get('upload'))
+ print(backup)
+

0 comments on commit 5a9ddea

Please sign in to comment.