#!/usr/bin/python import sys, os, stat, time import subprocess PATH_MYSQL_DATA = '/srv/mysql' PATH_AFS_BACKUP = '/afs/athena.mit.edu/contrib/sql/backup' PATH_AFS_BACKUP_VOL = '/afs/athena.mit.edu/contrib/sql/.backup' BACKUP_MAX_SIZE = 200*1024*1024 import MySQLdb, MySQLdb.cursors def new_cursor(group='client'): return MySQLdb.connect('localhost', read_default_group=group, cursorclass=MySQLdb.cursors.DictCursor).cursor() def use_db(cursor, db): cursor.execute('USE `%s`' % db) def get_dbs(cursor, prefix=None): cursor.execute('SHOW DATABASES' + (prefix and (' LIKE \'%s%%\'' % prefix) or '')) return [x.values()[0] for x in cursor.fetchall() if x.values()[0] != 'information_schema'] def get_db_tables(cursor, db): cursor.execute('SHOW TABLES FROM `%s`' % db) return [(db, x.values()[0]) for x in cursor.fetchall()] def get_db_tables_ft(cursor, db): r = [] for table in get_db_tables(cursor, db): cursor.execute('SHOW INDEXES FROM `%s`.`%s`' % table) r.extend(set([(db, x['Table']) for x in cursor.fetchall() if x['Index_type'] == 'FULLTEXT'])) return r def get_db_tables_status(cursor, db): cursor.execute('SHOW TABLE STATUS FROM `%s`' % db) return dict([((db, x['Name']), x) for x in cursor.fetchall()]) def get_db_mtime(cursor, db): db_mtimes = filter(lambda x: not x is None, [status['Update_time'] for table, status in get_db_tables_status(cursor, db).items()]) if len(db_mtimes): db_mtimes.sort(reverse=True) return db_mtimes[0] return 0 def get_db_size(cursor, db): return sum([status['Data_length'] for table, status in get_db_tables_status(cursor, db).items() if status['Data_length']]) def get_db_tables_engines(cursor, db): return [(table, status['Engine']) for table, status in get_db_tables_status(cursor, db).items()] def repair_table_quick(cursor, table): # REPAIR TABLE tbl QUICK # works when changing ft_min_word_len cursor.execute('REPAIR TABLE `%s`.`%s` QUICK' % table) def repair_tables_from_stdin(cursor): while True: data = sys.stdin.readline() if not data: break data = data.strip() table = eval(data) print table repair_table_quick(cursor, table) def db_backup_pre(cursor, db): db_size = get_db_size(cursor, db) if db_size < BACKUP_MAX_SIZE: if len(db.split('+')) <= 1: owner = 'root' else: owner = db.split('+')[0] db_mtime = get_db_mtime(cursor, db) db_path = '%s/%s/%s.sql.gz' % (PATH_AFS_BACKUP, owner.lower(), db) try: bk_mtime = os.stat(db_path)[stat.ST_MTIME] except: bk_mtime = 0 if not db_mtime: r = (False, 'empty database') elif int(time.mktime(db_mtime.timetuple())) > bk_mtime: r = (True, db_path) else: r = (False, 'mtime: %s < %s' % (str(db_mtime), time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bk_mtime)))) else: r = (False, 'size: %.2fM > %dM' % (db_size/1024/1024, BACKUP_MAX_SIZE/1024/1024)) return r def db_backup_pstrip(path): r = path if r.startswith(PATH_AFS_BACKUP): r = r[len(PATH_AFS_BACKUP):] if r and r[0] == '/': r = r[1:] return r def db_backup_mkdir(path): p = db_backup_pstrip(path) owner = p.split('/')[0] p_owner = '%s/%s' % (PATH_AFS_BACKUP, owner) p_owner_vol = '%s/%s/%s' % (PATH_AFS_BACKUP_VOL, owner[0], owner) if not os.path.islink(p_owner): os.symlink(p_owner_vol, p_owner) if not os.path.isdir(p_owner_vol): os.mkdir(p_owner_vol) subprocess.call(['fs', 'sa', p_owner_vol, 'system:anyuser', 'none']) subprocess.call(['fs', 'sa', p_owner_vol, 'system:authuser', 'none']) if owner == 'root': subprocess.call(['fs', 'sa', p_owner_vol, 'system:sql-backup', 'none']) #print owner #print ' ', p_owner #print ' ', p_owner_vol def tables_in_ibdata(cursor): for db in get_dbs(cursor): table_engines = get_db_tables_engines(cursor, db) innodb_tables = [x[0] for x in table_engines \ if x[1] and x[1].lower() == 'innodb'] for table in innodb_tables: path = '%s/%s/%s.ibd' % (PATH_MYSQL_DATA, table[0], table[1]) if not os.path.isfile(path): yield '`%s`.`%s`' % table if __name__ == '__main__': pass