mdserver-web/scripts/backup.py

301 lines
11 KiB
Python
Raw Permalink Normal View History

2019-02-20 05:01:51 -05:00
# coding: utf-8
#-----------------------------
# 网站备份工具
#-----------------------------
import sys
import os
2023-02-22 07:52:12 -05:00
import re
2024-12-03 07:16:19 -05:00
import time
2019-02-20 05:01:51 -05:00
if sys.platform != 'darwin':
os.chdir('/www/server/mdserver-web')
2024-12-03 07:16:19 -05:00
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
2019-02-20 05:01:51 -05:00
2024-12-03 07:16:19 -05:00
import core.mw as mw
import core.db as db
2019-02-20 05:01:51 -05:00
class backupTools:
2025-07-12 08:17:16 -04:00
def backupSite(self, name, count, echo=None):
exclude_dir_cmd = self.makeExcludeDirCmd(echo)
2019-02-20 05:01:51 -05:00
sql = db.Sql()
path = sql.table('sites').where('name=?', (name,)).getField('path')
startTime = time.time()
if not path:
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
2022-07-06 21:23:09 -04:00
log = "网站[" + name + "]不存在!"
print("★[" + endDate + "] " + log)
2019-02-20 05:01:51 -05:00
print(
"----------------------------------------------------------------------------")
return
2022-11-23 06:56:41 -05:00
backup_path = mw.getBackupDir() + '/site'
2019-02-20 05:01:51 -05:00
if not os.path.exists(backup_path):
2020-07-10 03:57:25 -04:00
mw.execShell("mkdir -p " + backup_path)
2019-02-20 05:01:51 -05:00
filename = backup_path + "/web_" + name + "_" + \
time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.tar.gz'
2022-10-26 05:47:40 -04:00
cmd = "cd " + os.path.dirname(path) + " && tar zcvf '" + \
2025-07-12 08:17:16 -04:00
filename + "' " + exclude_dir_cmd + " '" + os.path.basename(path) + "' > /dev/null"
2022-10-26 05:47:40 -04:00
# print(cmd)
mw.execShell(cmd)
2019-02-20 05:01:51 -05:00
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
2025-07-12 08:23:43 -04:00
# print(filename)
2019-02-20 05:01:51 -05:00
if not os.path.exists(filename):
2022-10-26 05:47:40 -04:00
log = "网站[" + name + "]备份失败!"
2022-07-06 21:23:09 -04:00
print("★[" + endDate + "] " + log)
2024-06-09 02:13:38 -04:00
print("----------------------------------------------------------------------------")
2019-02-20 05:01:51 -05:00
return
outTime = time.time() - startTime
pid = sql.table('sites').where('name=?', (name,)).getField('id')
2024-12-23 13:14:01 -05:00
sql.table('backup').add('type,name,pid,filename,add_time,size', ('0', os.path.basename(
2019-02-20 05:01:51 -05:00
filename), pid, filename, endDate, os.path.getsize(filename)))
2022-07-06 21:23:09 -04:00
log = "网站[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
2022-10-26 05:47:40 -04:00
mw.writeLog('计划任务', log)
2022-07-06 21:23:09 -04:00
print("★[" + endDate + "] " + log)
print("|---保留最新的[" + count + "]份备份")
print("|---文件名:" + filename)
2019-02-20 05:01:51 -05:00
# 清理多余备份
backups = sql.table('backup').where(
'type=? and pid=?', ('0', pid)).field('id,filename').select()
num = len(backups) - int(count)
if num > 0:
for backup in backups:
2020-07-10 03:57:25 -04:00
mw.execShell("rm -f " + backup['filename'])
2019-02-20 05:01:51 -05:00
sql.table('backup').where('id=?', (backup['id'],)).delete()
num -= 1
2022-07-06 21:23:09 -04:00
print("|---已清理过期备份文件:" + backup['filename'])
2019-02-20 05:01:51 -05:00
if num < 1:
break
2023-02-22 07:52:12 -05:00
def getConf(self, mtype='mysql'):
path = mw.getServerDir() + '/' + mtype + '/etc/my.cnf'
return path
2024-05-14 13:18:56 -04:00
def recognizeDbMode(self, mtype='mysql'):
conf = self.getConf(mtype)
con = mw.readFile(conf)
rep = r"!include %s/(.*)?\.cnf" % (mw.getServerDir() +'/'+ mtype +"/etc/mode",)
mode = 'none'
try:
data = re.findall(rep, con, re.M)
mode = data[0]
except Exception as e:
pass
return mode
2023-02-22 07:52:12 -05:00
# 数据库密码处理
def mypass(self, act, root):
conf_file = self.getConf('mysql')
mw.execShell("sed -i '/user=root/d' {}".format(conf_file))
mw.execShell("sed -i '/password=/d' {}".format(conf_file))
if act:
mycnf = mw.readFile(conf_file)
src_dump = "[mysqldump]\n"
sub_dump = src_dump + "user=root\npassword=\"{}\"\n".format(root)
if not mycnf:
return False
mycnf = mycnf.replace(src_dump, sub_dump)
if len(mycnf) > 100:
mw.writeFile(conf_file, mycnf)
return True
return True
2019-02-20 05:01:51 -05:00
def backupDatabase(self, name, count):
2020-07-10 03:57:25 -04:00
db_path = mw.getServerDir() + '/mysql'
2019-02-20 05:31:08 -05:00
db_name = 'mysql'
2020-07-10 03:57:25 -04:00
name = mw.M('databases').dbPos(db_path, 'mysql').where(
2019-02-20 05:31:08 -05:00
'name=?', (name,)).getField('name')
2019-02-20 05:01:51 -05:00
startTime = time.time()
2019-02-20 05:31:08 -05:00
if not name:
2019-02-20 05:01:51 -05:00
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
2022-07-06 21:23:09 -04:00
log = "数据库[" + name + "]不存在!"
print("★[" + endDate + "] " + log)
2019-02-20 05:01:51 -05:00
print(
2022-07-06 21:23:09 -04:00
"----------------------------------------------------------------------------")
2019-02-20 05:01:51 -05:00
return
2022-11-23 06:56:41 -05:00
backup_path = mw.getBackupDir() + '/database'
2019-02-20 05:01:51 -05:00
if not os.path.exists(backup_path):
2020-07-10 03:57:25 -04:00
mw.execShell("mkdir -p " + backup_path)
2019-02-20 05:01:51 -05:00
2019-02-20 05:31:08 -05:00
filename = backup_path + "/db_" + name + "_" + \
2019-02-20 05:01:51 -05:00
time.strftime('%Y%m%d_%H%M%S', time.localtime()) + ".sql.gz"
2020-07-10 03:57:25 -04:00
mysql_root = mw.M('config').dbPos(db_path, db_name).where(
2019-02-20 05:01:51 -05:00
"id=?", (1,)).getField('mysql_root')
2023-02-22 07:52:12 -05:00
my_cnf = self.getConf('mysql')
self.mypass(True, mysql_root)
2019-02-20 05:01:51 -05:00
2022-08-04 23:32:53 -04:00
# mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " +
# name + " | gzip > " + filename)
2022-08-04 23:02:45 -04:00
2022-11-27 11:08:36 -05:00
# mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " +
# name + " | gzip > " + filename)
2024-05-10 23:46:49 -04:00
# 开启一致性事务 会lock表
# cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --force --opt --default-character-set=utf8 " + \
# name + " | gzip > " + filename
2024-05-14 13:18:56 -04:00
option = ''
mode = self.recognizeDbMode('mysql')
if mode == 'gtid':
option = ' --set-gtid-purged=off '
2024-05-10 23:46:49 -04:00
2024-05-10 23:59:38 -04:00
# skip-opt 不会lock表
2024-05-14 13:18:56 -04:00
# --skip-opt --create-options
2024-05-17 02:05:25 -04:00
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf +" " + option +" --single-transaction -q --default-character-set=utf8mb4 " + \
2023-02-22 07:52:12 -05:00
name + " | gzip > " + filename
# print(cmd)
mw.execShell(cmd)
2022-08-04 23:32:53 -04:00
2019-02-20 05:01:51 -05:00
if not os.path.exists(filename):
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
2022-07-06 22:02:26 -04:00
log = "数据库[" + name + "]备份失败!"
2022-07-06 21:23:09 -04:00
print("★[" + endDate + "] " + log)
2019-02-20 05:01:51 -05:00
print(
2022-07-06 21:23:09 -04:00
"----------------------------------------------------------------------------")
2019-02-20 05:01:51 -05:00
return
2023-02-22 07:52:12 -05:00
self.mypass(False, mysql_root)
2019-02-20 05:01:51 -05:00
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
outTime = time.time() - startTime
2020-07-10 03:57:25 -04:00
pid = mw.M('databases').dbPos(db_path, db_name).where(
2019-02-20 05:31:08 -05:00
'name=?', (name,)).getField('id')
2019-02-20 05:01:51 -05:00
2024-12-23 13:14:20 -05:00
mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(
2019-02-20 05:01:51 -05:00
filename), pid, filename, endDate, os.path.getsize(filename)))
2022-08-03 09:10:37 -04:00
log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
2022-07-06 21:23:52 -04:00
mw.writeLog('计划任务', log)
2019-02-20 05:01:51 -05:00
print("★[" + endDate + "] " + log)
2022-07-06 21:23:52 -04:00
print("|---保留最新的[" + count + "]份备份")
2022-07-06 21:23:09 -04:00
print("|---文件名:" + filename)
2019-02-20 05:01:51 -05:00
# 清理多余备份
2020-07-10 03:57:25 -04:00
backups = mw.M('backup').where(
2019-02-20 05:01:51 -05:00
'type=? and pid=?', ('1', pid)).field('id,filename').select()
num = len(backups) - int(count)
if num > 0:
for backup in backups:
2020-07-10 03:57:25 -04:00
mw.execShell("rm -f " + backup['filename'])
mw.M('backup').where('id=?', (backup['id'],)).delete()
2019-02-20 05:01:51 -05:00
num -= 1
2022-07-06 21:23:52 -04:00
print("|---已清理过期备份文件:" + backup['filename'])
2019-02-20 05:01:51 -05:00
if num < 1:
break
def backupSiteAll(self, save):
2020-07-10 03:57:25 -04:00
sites = mw.M('sites').field('name').select()
2019-02-20 05:01:51 -05:00
for site in sites:
self.backupSite(site['name'], save)
def backupDatabaseAll(self, save):
2020-07-10 03:57:25 -04:00
db_path = mw.getServerDir() + '/mysql'
2019-02-20 05:31:08 -05:00
db_name = 'mysql'
2020-07-10 03:57:25 -04:00
databases = mw.M('databases').dbPos(
2019-02-20 05:31:08 -05:00
db_path, db_name).field('name').select()
2019-02-20 05:01:51 -05:00
for database in databases:
self.backupDatabase(database['name'], save)
2022-12-18 09:44:59 -05:00
def findPathName(self, path, filename):
f = os.scandir(path)
l = []
for ff in f:
if ff.name.find(filename) > -1:
l.append(ff.name)
return l
2025-07-12 08:12:17 -04:00
def makeExcludeDirCmd(self,echo):
exclude_dirs = []
crontab_list = mw.M('crontab').where('echo=?', (echo,)).field('attr').find()
if crontab_list:
attr = crontab_list['attr']
if attr != "":
ed_arrs = attr.split("\n")
for ed in ed_arrs:
exclude_dirs.append(ed.strip())
cmd = ""
for v in exclude_dirs:
cmd += " --exclude='"+v+"'"
return cmd
def backupPath(self, path, count, echo=None):
exclude_dir_cmd = self.makeExcludeDirCmd(echo)
2025-07-12 08:12:31 -04:00
# print(exclude_dir_cmd)
2022-12-18 09:44:59 -05:00
mw.echoStart('备份')
2022-12-18 08:11:15 -05:00
backup_path = mw.getBackupDir() + '/path'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
dirname = os.path.basename(path)
fname = 'path_{}_{}.tar.gz'.format(
dirname, mw.formatDate("%Y%m%d_%H%M%S"))
dfile = os.path.join(backup_path, fname)
2022-12-18 09:44:59 -05:00
p_size = mw.getPathSize(path)
stime = time.time()
2025-07-12 08:12:17 -04:00
cmd = "cd " + os.path.dirname(path) + " && tar zcvf '" + dfile + "' " + exclude_dir_cmd + " '" + dirname + "' 2>{err_log} 1> /dev/null".format(
2022-12-18 08:11:15 -05:00
err_log='/tmp/backup_err.log')
2025-07-12 08:12:17 -04:00
# print(cmd)
2022-12-18 08:11:15 -05:00
mw.execShell(cmd)
2022-12-18 09:44:59 -05:00
tar_size = os.path.getsize(dfile)
mw.echoInfo('备份目录:' + path)
mw.echoInfo('目录已备份到:' + dfile)
mw.echoInfo("目录大小:{}".format(mw.toSize(p_size)))
mw.echoInfo("开始压缩文件:{}".format(mw.formatDate(times=stime)))
mw.echoInfo("文件压缩完成,耗时{:.2f}秒,压缩包大小:{}".format(
time.time() - stime, mw.toSize(tar_size)))
mw.echoInfo('保留最新的备份数:' + count + '')
backups = self.findPathName(backup_path, 'path_{}'.format(dirname))
num = len(backups) - int(count)
backups.sort()
if num > 0:
for backup in backups:
abspath_bk = backup_path + "/" + backup
mw.execShell("rm -f " + abspath_bk)
2024-06-09 02:13:38 -04:00
mw.echoInfo("已清理过期备份文件:" + abspath_bk)
2022-12-18 09:44:59 -05:00
num -= 1
if num < 1:
break
mw.echoEnd('备份')
2019-02-20 05:01:51 -05:00
if __name__ == "__main__":
backup = backupTools()
2022-12-17 14:15:39 -05:00
stype = sys.argv[1]
if stype == 'site':
2019-02-20 05:01:51 -05:00
if sys.argv[2] == 'ALL':
backup.backupSiteAll(sys.argv[3])
else:
2025-07-12 08:12:17 -04:00
backup.backupSite(sys.argv[2], sys.argv[3], sys.argv[4])
2022-12-17 14:15:39 -05:00
elif stype == 'database':
2019-02-20 05:01:51 -05:00
if sys.argv[2] == 'ALL':
backup.backupDatabaseAll(sys.argv[3])
else:
backup.backupDatabase(sys.argv[2], sys.argv[3])
2022-12-17 14:15:39 -05:00
elif stype == 'path':
2025-07-12 08:12:17 -04:00
backup.backupPath(sys.argv[2], sys.argv[3], sys.argv[4])