#! /usr/bin/python
#-*- encoding: iso-8859-1 -*-

#
# Script de backup (dump/gzip) des bases Easy.
#
# Yann GROSSEL - 2004.02.18
#

path   = '/data/Databases/mysql1/'

m_host = 'mysql1.co.fr.clara.net'
m_user = 'user'
m_pass = 'password'

bad_tables = [ 'group', 'references', 'option' ]				# Liste de tables non dumpables avec mysqldump

#

import os, sys, MySQLdb as mysql, time, errno

def debug(str):
	if sys.stdout.isatty():
		sys.stdout.write(str + "\n")

def escape(str):
	# Escape une chaine de caractère. La méthode utilisée ( $'...' ) ne fonctionne que si /bin/sh est un BASH !
	r = ''
	for a in str: r += '\\x%.2x' % ord(a)
	return "$'%s'" % (r)

os.umask(0077)

tl = time.localtime()													# Tuple de l'heure locale
ts = time.mktime((tl[0], tl[1], tl[2], 12, 0, 0, 0, 0, 0))	# Timestamp de la journée à 12:00
ts -= 86400 * 7															# Timestamp d'il y a une semaine a 12:00
tl = time.localtime(ts)													# Tuple correspondant

folder    = path + '%s' % time.strftime('%Y-%m-%d')			# Nom du répertoire quotidien
oldfolder = path + '%s' % time.strftime('%Y-%m-%d', tl)		# Nom du répertoire quotidien d'il y a 7 jours

try: os.mkdir(folder, 0700)											# On essaie de le créer
except OSError,e :
	if e[0] == errno.EEXIST:
			debug("Folder %s already present, aborting" % (folder))
			sys.exit(0)							# S'il existe déjà, on quitte silencieusement.
	else: raise

os.chdir(folder)

debug("Will dump bases in %s" % (folder))

debug("Connecting to mysql server...")

s = mysql.connect(host = m_host, user = m_user, passwd = m_pass)

c = s.cursor()

# On commence par trouver toutes les tables à dumper.
# On le fait les requetes SQL maintenant, et on mémorise les résultats, car comme le dump des
# bases prend des heures (sans exagérer), la connexion MySQL aura timeouté si on fait des requetes MySQL
# plus tard, ce qui générera une exception... que je n'ai pas envie de gérer :)

bases = {}

debug("Executing SHOW DATABASES")

c.execute("SHOW DATABASES")

for base in c.fetchall(): bases[base[0]] = []

n_bases = bases.keys()
n_bases.sort()

debug("Executing SHOW TABLES for each database")

for base in n_bases:
	c.execute("SHOW TABLES FROM `%s`" % base)
	for t in c.fetchall():
		bases[base].append(t[0])

s.close()	# Plus besoin de la connexion à la base MySQL.

del s

# On dump les bases.

for base in n_bases:

	ret = 0
	n = 0

	for table in bases[base]:

		if table.lower() in bad_tables: continue	# La table n'est pas dumpable: tant pis, on ne la backup pas.

		debug("Dumping %s.%s" % (base, table))

		ret = os.system("/usr/bin/mysqldump --skip-lock-tables -h %s -u %s --password=%s %s %s >> %s.sql" % (m_host, m_user, m_pass, escape(base), escape(table), escape(base)))
		if ret != 0: break

		n += 1

	if n == 0: continue	# Aucune table dumpée pour la base courante, on passe à la base suivante

	if ret == 0:
		# On compresse le fichier de dump.
		debug("Compressing %s.sql" % (base))
		ret = os.system("/bin/gzip -9 %s.sql && chmod 400 %s.sql.gz" % (escape(base), escape(base)))
		# On efface le fichier du backup d'il y a 7 jours. C'est super moche (os.system) mais bon...
		if ret == 0: os.system("/bin/rm -f %s/%s.sql.gz" % (oldfolder, escape(base)))

	else:
		# Le dump n'a pas fonctionné correctement. On émet un message d'alerte indiquant la base.
		sys.stderr.write("WARNING: mysqldump exit code was %d for base %s !\n\n" % (ret, base))

	time.sleep(2)															# Une petite pause supplémentaire pour ménager Provence

# vim: set ts=3 sw=3:
