В дополнение к скриптуPython: скрипт инкрементального или полного бекапа файлов — второй, который выполняет полное копирование баз данных.
В целях «совместимости» с первым скриптом — данные так же хранятся в отдельных daily и weekly директориях, хотя в отличии от файлов тут не выполняется инкрементальный бекап — базы небольшие, а после сжатия bzip2 совсем маленькие:
# ls -l /home/setevoy/backups/setevoy/daily/ total 16 drwxr-xr-x 2 root root 4096 Oct 13 23:15 2014-10-13-files drwxr-xr-x 2 root root 4096 Oct 14 04:00 2014-10-14-files drwxr-xr-x 2 root root 4096 Oct 15 17:49 2014-10-15-database drwxr-xr-x 2 root root 4096 Oct 15 08:44 2014-10-15-files
Скрипт выполняет «обход» по каждой базе, к которой есть доступ у пользователя (за исключением баз, перечисленных в переменной EXCLUDE):
# ls -lh /home/setevoy/backups/setevoy/weekly/2014-10-15-database/ total 3.1M -rw-r--r-- 1 root root 805 Oct 15 18:02 autocomtestdb.sql.bz2 -rw-r--r-- 1 root root 417 Oct 15 18:02 mysqlslap.sql.bz2 -rw-r--r-- 1 root root 4.9K Oct 15 18:02 old_money_db1.sql.bz2 -rw-r--r-- 1 root root 3.0M Oct 15 18:02 setevoy_forum.sql.bz2 -rw-r--r-- 1 root root 5.1K Oct 15 18:02 setevoy_money_db1.sql.bz2 -rw-r--r-- 1 root root 89K Oct 15 18:02 setevoyorgua_db1.sql.bz2 -rw-r--r-- 1 root root 843 Oct 15 18:02 setevoy_test.sql.bz2 -rw-r--r-- 1 root root 1.8K Oct 15 18:02 themega_chars.sql.bz2
MariaDB [(none)]> select user, db from mysql.db; +-------------+-------------------+ | user | db | +-------------+-------------------+ | setevoy | setevoy_test | | setevoy | autocomtestdb | ... | vexim | vexim | | worlddesign | worlddesign_db1 | | zabbix | zabbix | +-------------+-------------------+ 16 rows in set (0.00 sec)
К примеру, база RTFM занимает:
# du -sh /var/lib/mysql/rtfm_db1/ 47M /var/lib/mysql/rtfm_db1/
А её бекап после сжатия:
# ls -lh ../backups/rtfm/daily/2014-10-16-database/ total 3.2M -rw-r--r-- 1 root root 3.2M Oct 16 03:00 rtfm_db1.sql.bz2
Так же, тут нет функции для удаления старых каталогов с данными — этим занимается функция clear_old_dirs() из скрипта files_backup.py:
clear_old_dirs(user, day, count, host, bkptype)
Deletes old directories. Depending on [count] — can store 7
(for daily) or 4 (for weekly) copies, including today’s archive.
Поэтому, скрипт запускается по cron-у перед выполнением бекапа файлов:
# crontab -l ... 0 03 * * * /home/setevoy/opt/mysql_backup.py 0 04 * * * /home/setevoy/opt/files_backup.py
Лог выглядит так:
mysql_backup.py - INFO [2014-10-15 18:29:47,028] Backup started at: 2014-10-15 18:29:47 mysql_backup.py - INFO [2014-10-15 18:29:47,041] Today is Wed, will use backuptype daily. mysql_backup.py - INFO [2014-10-15 18:29:47,042] Directory /home/setevoy/backups/setevoy/daily/2014-10-15-database/ created mysql_backup.py - INFO [2014-10-15 18:29:47,055] Database backup complete: database setevoy_test saved to /home/setevoy/backups/setevoy/daily/2014-10-15-database/setevoy_test.sql.bz2 mysql_backup.py - INFO [2014-10-15 18:29:47,060] Database mysqlslap in exclude list, skip ... mysql_backup.py - INFO [2014-10-15 18:29:47,068] Backup /home/setevoy/backups/hudeem/daily/2014-10-15-database/hudeem_db.sql.bz2 already present, skip .... mysql_backup.py - INFO [2014-10-15 18:29:57,337] Email sent from ['[email protected]'] to ['[email protected]']. mysql_backup.py - INFO [2014-10-15 18:29:57,337] Backup finished at: 2014-10-15 18:29:57
И сам скрипт:
#!/usr/bin/env python
'''
Create full backup every user's database.
Creation date: 15.10.2014
Last modify date: 16.10.2014
'''
import MySQLdb
import socket
import os
import logging
import time
import subprocess
import sys
import smtplib
import mimetypes
import email
import email.mime.application
import socket
BACKDIR = '/home/setevoy/backups/'
LOGPATH = '/var/log/database_backup.log'
DBROOT = 'root'
_DBROOTPW = 'PasswordHere'
DBHOST = 'localhost'
# exclude databases list - will not be added to backup
EXCLUDE = 'mysqlslap'
HOST = socket.gethostname()
logging.basicConfig(format = '%(filename)s - %(levelname)-3s [%(asctime)s] %(message)s ', filename=LOGPATH, level=logging.DEBUG)
def dir_bkptype(day):
'''On Sunday - create full weekly backup with 'full_backup()';
other days - incremental backup with inc_backup()';
also used in 'back_dir_create()' to create BACKDIR/weekly or daily;
also used in 'clear_old_dirs()' for deleting old directories.'''
if day == 'Mon':
bkptype = 'weekly'
else:
bkptype = 'daily'
logging.info('Today is %s, will use backuptype %s.' % (day, bkptype))
return(bkptype)
def back_dir_create(user, bkptype):
'''Creates new directory for each day;
depending on data recieved from 'dir_bkptype()'
can create 'weekly' or 'daily' directory.'''
dirname = os.path.join(BACKDIR, user, bkptype, '%s-database/' % time.strftime('%Y-%m-%d'))
if not os.path.exists(dirname):
os.makedirs(dirname)
logging.info('Directory %s created' % dirname)
return(dirname)
def mysql_connect(dbroot, dbhost, dbrootpw):
'''Calls in 'run_backup()' with [DBROOT], [_DBROOTPW] and [DBHOST]
in arguments.
Creates object [cursor] for use in select_user_dbs()'''
db = MySQLdb.connect(dbhost, dbroot, dbrootpw)
cursor = db.cursor()
return(cursor)
def select_user_dbs(cursor):
'''Calls with [cursor] from 'mysql_connect()';
returns two lists like:
for users - ['setevoy', 'setevoy', 'hudeem', [...] 'vexim', 'worlddesign', 'zabbix']
and their databases: ['setevoy_test', 'autocomtestdb', 'hudeem_db', [...] 'vexim', 'worlddesign_db1', 'zabbix']'''
users_list = []
database_list = []
cursor.execute('select user, db from mysql.db')
for line in cursor.fetchall():
users_list.append(line[0])
database_list.append(line[1])
return(users_list, database_list)
cursor.close()
def mysql_dump(dbroot, dbrootpw, database, archname):
'''Calls in 'run_backup()' to create dump'''
subprocess.call('mysqldump -u %s -p%s %s | bzip2 > %s' % (dbroot, dbrootpw, database, archname), shell=True)
logging.info('Database backup complete: database %s saved to %s' % (database, archname))
def sendmail():
'''Send email report to [to] with used disk space information.'''
sender = '[email protected]'
to = ['[email protected]']
msg = email.mime.Multipart.MIMEMultipart()
msg['Subject'] = ('Database backup report')
msg['From'] = 'Backup Manager <[email protected]>'
msg['To'] = 'root <[email protected]>'
body = email.mime.Text.MIMEText("""
Databases backup on %s finished.
""" % (HOST))
msg.attach(body)
smtpconnect = smtplib.SMTP('localhost:25')
#smtpconnect.set_debuglevel(1)
smtpconnect.sendmail(sender, to, msg.as_string())
smtpconnect.quit()
logging.info('Email sent from ['%s'] to %s.' % (sender, to))
def run_backup():
'''Main function.
cursor - creates 'cursor' object from 'mysql_connect()' first;
user, database - receives lists (users_list[] and database_list[]) from 'select_user_dbs()';
bkptype - determines backup type (weekly, daily) from 'dir_bkptype()'.
For each user looks for it's databases, creates directory,
and if there is no backup ('archname') yet - calls 'mysql_dump()' function.'''
cursor = mysql_connect(DBROOT, DBHOST, _DBROOTPW)
user, database = select_user_dbs(cursor)
bkptype = dir_bkptype(curday)
curday = time.strftime('%a')
for user, database in zip(user, database):
if database not in EXCLUDE:
backup_dir = back_dir_create(user, bkptype)
if backup_dir:
archname = (backup_dir + database + '.sql.bz2')
if not os.path.isfile(archname):
mysql_dump(DBROOT, _DBROOTPW, database, archname)
else:
logging.info('Backup %s already present, skip' % archname)
else:
logging.info('Database %s in exclude list, skip' % database)
sendmail()
if __name__ == '__main__':
starttime = time.strftime('%Y-%m-%d %H:%M:%S')
logging.info('Backup started at: %s' % starttime)
run_backup()
finishtime = time.strftime('%Y-%m-%d %H:%M:%S')
logging.info('Backup finished at: %s' % finishtime)




