action / bt-source /panel /class /panelModel /whole_machine_backupModel.py
GGSheng's picture
feat: deploy Gemma 4 to hf space
020c337 verified
# coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: sww <sww@bt.cn>
# -------------------------------------------------------------------
# 备份
# ------------------------------
import datetime
import json
import os
import time
import sys
import concurrent.futures
import threading
import hashlib
if '/www/server/panel/class' not in sys.path:
sys.path.insert(0, '/www/server/panel/class')
if '/www/server/panel' not in sys.path:
sys.path.insert(0, '/www/server/panel')
import public
import panelMysql
import db_mysql
import database
import psutil
import ajax
from firewallModel.comModel import main as firewall_main
from safeModel.firewallModel import main as safe_firewall_main
from CloudStoraUpload import CloudStoraUpload # 引入云存储上传模块
class main:
sys_config = public.M('config').where("id=1", ()).find()
setting_path='{}/data/whole_machine_backup_settings.json'.format(public.get_panel_path())
if os.path.exists(setting_path):
sys_backup_path = json.loads(public.ReadFile(setting_path))['sys_backup_path']
else:
sys_backup_path = '/www/backup/'
if sys_config and sys_config.get('backup_path'):
sys_backup_path = sys_config.get('backup_path')
sys_backup_path = sys_backup_path if sys_backup_path.endswith('/') else sys_backup_path + '/'
backup_path = '{}whole_machine_backup/'.format(sys_backup_path)
backup_log_path = '{}log/'.format(backup_path)
all_backup_config_path = os.path.join(public.get_panel_path(), 'config/whole_machine_backup.json')
time_str = public.format_date()
lock = None
executor = None
functions = []
backup_config = {}
reduction_config = {}
backup_save_config = backup_path + '{}/backup.json'
id_config = None
backup_record = None
reduction_log_path = None
backup_task_data = None
logs_file = '{}whole_machine_backup.log'.format(backup_path)
status = None
error_num = 0
access_num = 0
def __init__(self):
if not os.path.exists(self.backup_path):
public.ExecShell("mkdir -p {}".format(self.backup_path))
public.ExecShell("chmod -R 755 {}".format(self.backup_path))
public.ExecShell("chown -R www:www {}".format(self.backup_path))
if not os.path.exists(self.all_backup_config_path):
public.writeFile(self.all_backup_config_path, json.dumps({}))
self.config = json.loads(public.readFile(self.all_backup_config_path))
def print_log(self, log: str):
time_str = time.strftime('%Y-%m-%d %H:%M:%S')
log = "{} $$ {}".format(time_str, log)
public.writeFile(self.logs_file, log + "\n", 'a+')
def write_config(self, id, conf):
self.config[id] = conf
public.writeFile(self.all_backup_config_path, json.dumps(self.config))
def get_md5(self, path):
try:
if os.path.exists(path) and os.path.isfile(path):
return public.FileMd5(path)
return False
except:
return False
# ======================================================================
# 交互层级
# ======================================================================
# 获取恢复对比列表
# 获取备份任务列表
def get_task_list(self, get=None):
try:
if not hasattr(get, 'page') or get.page == '':
get.page = 1
if not hasattr(get, 'limit') or get.limit == '':
get.limit = 10
get.page, get.limit = int(get.page), int(get.limit)
result = []
ids = ['log']
for k, v in self.config.items():
info = {'id': k}
ids.append(k)
info.update(v)
info['backup_path'] = info['backup_path'] + '.tar.gz'
result.append(info)
for i in os.listdir(self.backup_path):
if os.path.isdir(os.path.join(self.backup_path, i)) and i not in ids:
public.ExecShell('rm -rf {}'.format(os.path.join(self.backup_path, i)))
result = sorted(result, key=lambda x: x['addtime'], reverse=True)
# 做分页处理
data = public.get_page(len(result), get.page, get.limit)
data['data'] = result[get.page * get.limit - get.limit:get.page * get.limit]
return public.returnMsg(True, data)
except:
self.print_log(public.get_error_info())
return public.returnMsg(False, '获取任务列表失败!')
# 获取任务信息
def get_task_info(self, get):
if not hasattr(get, 'id'):
return public.returnMsg(False, '参数错误,缺少参数id!')
if get.id not in self.config:
return public.returnMsg(False, '任务不存在!')
task_config_path = self.config[get.id]["task_config"]
try:
task_config = json.loads(public.readFile(task_config_path))
if isinstance(task_config['backup_config'], str):
task_config['backup_config'] = json.loads(task_config['backup_config'])
if isinstance(task_config['reduction_config'], str):
task_config['reduction_config'] = json.loads(task_config['reduction_config'])
except:
return public.returnMsg(False, '任务配置文件读取失败!')
return public.returnMsg(True, task_config)
# 删除任务
def del_task(self, get):
if not hasattr(get, 'id'):
return public.returnMsg(False, '参数错误,缺少参数id!')
if get.id not in self.config:
return public.returnMsg(False, '任务不存在!')
if "task_id" in self.config[get.id]:
count = public.M('tasks').where("id=? nd status!='1'", (self.config[get.id]["task_id"],)).count()
if count:
return public.returnMsg(False, '任务正在执行中,请等待执行结束后再删除!')
del self.config[get.id]
public.ExecShell("rm -rf {}*".format(os.path.join(self.backup_path, get.id)))
public.ExecShell("rm -rf {}".format(os.path.join(self.backup_log_path, get.id + "_backup.log")))
public.ExecShell("rm -rf {}".format(os.path.join(self.backup_log_path, get.id + "_reduction.log")))
public.writeFile(self.all_backup_config_path, json.dumps(self.config))
return public.returnMsg(True, '删除成功!')
# 获取备份日志
def get_backup_log(self, get):
if not hasattr(get, 'id'):
return public.returnMsg(False, '参数错误,缺少参数id!')
if get.id not in self.config:
return public.returnMsg(False, '任务不存在,请刷新后重试!!')
backup_log_path = self.config[get.id]["backup_log_path"]
if not os.path.exists(backup_log_path):
return public.returnMsg(False, '未检测到日志!')
log = public.readFile(backup_log_path)
if log:
try:
log = json.loads(log)
return public.returnMsg(True, log)
except:
pass
return public.returnMsg(False, "日志格式话出错!")
# 获取还原日志
def get_reduction_log(self, get):
if not hasattr(get, 'id'):
return public.returnMsg(False, '参数错误,缺少参数id!')
if get.id not in self.config:
return public.returnMsg(False, '任务不存在,请刷新后重试!')
reduction_log_path = self.config[get.id]["reduction_log_path"]
if not os.path.exists(reduction_log_path):
return public.returnMsg(False, '未检测到日志!')
log = public.readFile(reduction_log_path)
if log:
try:
log = json.loads(log)
return public.returnMsg(True, log)
except:
pass
return public.returnMsg(False, "日志格式话出错!")
# 获取备份数据列表
def get_data_list(self, get):
"""
获取备份数据列表
:param get:
"""
result = {}
# 环境备份列表
result["env_list"] = self.get_env_list()
# 数据备份列表
result["data_list"] = {}
result["data_list"]["site_list"] = self.get_site_list()
result["data_list"]["sql_list"] = self.get_sql_list()
result["data_list"]["ftp_list"] = self.get_ftp_list()
result["data_list"]["terminal_list"] = self.get_terminal_list()
result["data_list"]["crontab_list"] = self.get_crontab_list()
result["data_list"]['safety'] = {
"port_list": [{"name": "端口规则", "status": 1, "backup_status": 0, "reduction_status": 0}],
"ip_list": [{"name": "IP规则", "status": 1, "backup_status": 0, "reduction_status": 0}],
"port_redirect": [{"name": "端口转发", "status": 1, "backup_status": 0, "reduction_status": 0}],
"area_list": [{"name": "地区规则", "status": 1, "backup_status": 0, "reduction_status": 0}],
"ssh_config": [{"name": "ssh配置", "status": 1, "backup_status": 0, "reduction_status": 0}],
'words': [{"name": "违规词检测-关键词", "status": 1, "backup_status": 0, "reduction_status": 0}],
}
# 配置备份列表
# result["config_list"] = self.get_config_list()
return result
def get_site_list(self):
"""
获取站点列表
:return: dict
"""
# result = {'php': [], 'proxy': [], 'java': [], 'node': [], 'go': [], 'python': [], 'net': [], 'html': [], 'other': []}
result = {'php': [], 'proxy': []}
# now_list = ['php', 'proxy']
data = public.M('sites').field('name,project_type,id,ps').select()
if isinstance(data, str):
return {}
for i in data:
if i['project_type'].lower() not in result.keys():
continue
result[i['project_type'].lower()].append({"name": i['name'], "status": 1, "backup_status": 0, "reduction_status": 0, "id": i['id'], 'ps': i['ps']})
cont = [i for i, j in result.items() if not j]
[result.pop(i) for i in cont]
return result
def get_sql_list(self):
"""
获取数据库列表
:return: dict
"""
result = {}
data = public.M('databases').field('name,type,id').select()
result['mysql'] = []
if isinstance(data, str):
return {}
for i in data:
if i['type'].lower() not in result:
continue
if i['type'].lower() in ['mysql']:
result[i['type'].lower()].append({"name": i['name'], "status": 1, "backup_status": 0, "reduction_status": 0, "id": i['id']})
# else:
# result[i['type'].lower()].append({"name": i['name'], "status": 3, "backup_status": 0, "reduction_status": 0, "id": i['id']})
# result['redis'] = [{"name": "redis", "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['sqlite'] = []
# db_file_path = '{}/data/db_model.json'.format(public.get_panel_path())
# if os.path.exists(db_file_path):
# db_data = json.loads(public.readFile(db_file_path))
# for i in db_data:
# result['sqlite'].append({"name": os.path.basename(i), "status": 3, "backup_status": 0, "reduction_status": 0})
return result
def get_ftp_list(self):
"""
获取ftp列表
:return: list
"""
result = []
data = public.M('ftps').field('name,id').select()
if isinstance(data, str):
return []
for i in data:
result.append({"name": i['name'], "status": 1, "backup_status": 0, "reduction_status": 0, "id": i['id']})
return result
def get_env_list(self):
result = {}
# 获取web服务状态
web_type = public.get_webserver()
if web_type == 'nginx':
web_setup = os.path.exists('/www/server/nginx')
web_version = public.readFile('/www/server/nginx/version.pl').strip() if web_setup else ""
elif web_type == 'apache':
web_setup = os.path.exists('/www/server/apache')
web_version = public.readFile('/www/server/apache/version.pl').strip() if web_setup else ""
else:
web_setup = False
web_version = ""
result[web_type] = [{"setup": web_setup, "name": web_version, "status": 1 if web_setup else 0, "backup_status": 0, "reduction_status": 0}]
# 获取数据库版本,及安装状态
mysql_path = "/www/server/mysql"
mysql_setup = os.path.exists(mysql_path)
mysql_version = public.readFile(mysql_path + "/version.pl").strip() if mysql_setup else ""
result['mysql'] = [{"setup": mysql_setup, "name": mysql_version, "status": 1 if mysql_setup else 0, "backup_status": 0, "reduction_status": 0, "primitive_data": "0"}]
# 获取 ftp 服务状态
ftp_path = "/www/server/pure-ftpd"
ftp_setup = os.path.exists(ftp_path)
ftp_version = public.readFile(ftp_path + "/version.pl").strip() if ftp_setup else ""
result['ftp'] = [{"setup": ftp_setup, "name": ftp_version, "status": 1 if ftp_setup else 0, "backup_status": 0, "reduction_status": 0}]
# 获取php版本
php_path = "/www/server/php"
php_list = ['52', '53', '54', '55', '56', '70', '71', '72', '73', '74', '80', '81', '82', '83']
for i in php_list:
if 'php' not in result:
result['php'] = []
try:
int(i)
result['php'].append({"name": i, "status": 1 if os.path.exists('/www/server/php/{}/bin'.format(i)) else 0, "backup_status": 0, "reduction_status": 0,
'setup': os.path.exists('/www/server/php/{}/bin'.format(i))})
except:
pass
# mongo_path = "/www/server/mongodb"
# mongo_setup = os.path.exists(mongo_path)
# mongo_version = public.readFile(mongo_path + "/version.pl").strip() if mongo_setup else ""
# result['mongo'] = [{"setup": mongo_setup, "name": mongo_version, "status": 3, "backup_status": 0, "reduction_status": 0}]
#
# redis_path = "/www/server/redis"
# redis_setup = os.path.exists(redis_path)
# redis_version = public.readFile(redis_path + "/version.pl").strip() if redis_setup else ""
# result['redis'] = [{"setup": redis_setup, "name": redis_version, "status": 3, "backup_status": 0, "reduction_status": 0}]
#
# pgsql_path = "/www/server/pgsql"
# pgsql_setup = os.path.exists(pgsql_path)
# pgsql_version = public.readFile(pgsql_path + "/data/PG_VERSION").strip() if pgsql_setup else ""
# result['pgsql'] = [{"setup": pgsql_setup, "name": pgsql_version, "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['python'] = [{"setup": False, "name": '', "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['java'] = [{"setup": False, "name": '', "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['node'] = [{"setup": False, "name": '', "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['.net'] = [{"setup": False, "name": '', "status": 3, "backup_status": 0, "reduction_status": 0}]
# result['tomcat'] = [{"setup": False, "name": '', "status": 3, "backup_status": 0, "reduction_status": 0}]
return result
def get_config_list(self):
result = {"alarm": [{"name": "面板告警", "status": 3, "backup_status": 0, "reduction_status": 0}], }
return result
def get_terminal_list(self):
result = []
result.append({"name": "服务器列表", "status": 1, "backup_status": 0, "reduction_status": 0})
result.append({"name": "常用命令", "status": 1, "backup_status": 0, "reduction_status": 0})
return result
def get_crontab_list(self):
result = []
result.append({"name": "计划任务列表", "status": 1, "backup_status": 0, "reduction_status": 0})
return result
# ======================================================================
# 创建任务
# ======================================================================
def create_task(self, get):
"""
创建备份任务
:param get:
type: 1:备份 2还原
id: 还原配置id
conf: 备份配置信息
:return:
"""
if not hasattr(get, 'type'):
return public.returnMsg(False, '参数错误,缺少参数type!')
if not hasattr(get, 'id') and get.type == '2' and not hasattr(get, 'reduction_config'):
return public.returnMsg(False, '参数错误,缺少参数reduction_config或id!')
if get.type == '1' and not hasattr(get, 'backup_config'):
return public.returnMsg(False, '参数错误,缺少参数backup_config!')
if not hasattr(get, "backup_type") or get.backup_type == "":
get.backup_type = "整机备份"
if not hasattr(get, "next_exec_time") or get.next_exec_time == "":
get.next_exec_time = "0"
# get.storage_type="ftp"
# 设置存储类型,默认为 "local"
storage_type = get.storage_type if hasattr(get, 'storage_type') else 'local'
if get.type == '2' and storage_type !='local':
return public.returnMsg(False, '暂时不还原云存储文件!')
try:
# 根据任务类型设置任务名称
if get.type == '1':
name = "备份任务"
else:
name = "还原任务"
# 检查是否存在重复任务(同名任务且未完成)
task_data = public.M('tasks').where("name = ? AND status != ?", ("{}".format(name), "1")).find()
if task_data:
return public.returnMsg(False, '已有重复的任务了,请上一个备份任务执行完成后再进行添加新的备份任务!')
# 初始化任务参数
get.type = get.type if hasattr(get, 'type') else '1'
get.backup_sql_time = int(get.backup_sql_time) if hasattr(get, 'backup_sql_time') else 0
if int(get.type) == 1: # 备份任务
if get.next_exec_time not in [0, '0']:
exec_time=datetime.datetime.fromtimestamp(int(get.next_exec_time)).strftime("%Y-%m-%d %H:%M:%S")
else:
exec_time="暂未执行"
# 确保 backup_config 为 JSON 格式
get.backup_config = get.backup_config if isinstance(get.backup_config, str) else json.dumps(get.backup_config)
# 生成唯一任务 ID
get.id = public.GetRandomString(16)
# 设置任务相关文件路径
task_config_path = os.path.join(self.backup_path, get.id, "task_config.json")
reduction_task_config_path = os.path.join(self.backup_path, get.id + "_reduction.json")
storage_backup_path=""
if storage_type !='local':
name="{}.tar.gz".format(get.id)
cloud_name=get.storage_type
import CloudStoraUpload
c = CloudStoraUpload.CloudStoraUpload()
c.run(cloud_name)
url = ''
backup_path = c.obj.backup_path
storage_backup_path = os.path.join(backup_path, "whole_machine_backup",name)
# 定义任务配置
conf = {
"backup_sql_time": get.backup_sql_time,
"addtime": time.strftime('%Y-%m-%d %H:%M:%S'),
"backup_path": os.path.join(self.backup_path, get.id),
"backup_log_path": os.path.join(self.backup_log_path, get.id + "_backup.log"),
"reduction_log_path": os.path.join(self.backup_log_path, get.id + "_reduction.log"),
"status": 0, # -1 备份中 0 等待备份 1 备份完成 2 备份失败 3 还原中 4 还原完成 5 还原失败
"task_config": task_config_path,
"reduction_task_config_path": reduction_task_config_path,
"exec_time": exec_time,
"backup_type": get.backup_type,
"name": get.get('name', get.backup_type),
"storage_type":storage_type,
"storage_backup_path":storage_backup_path
}
# 创建备份任务目录
if not os.path.exists(os.path.join(self.backup_path, get.id)):
public.ExecShell("mkdir -p {}".format(os.path.join(self.backup_path, get.id)))
# 写入任务配置文件
task_config = {
"backup_config": get.backup_config,
"reduction_config": {}
}
public.writeFile(task_config_path, json.dumps(task_config))
# 将任务配置写入全局配置
self.write_config(get.id, conf)
# 如果设置了定时执行时间,加入定时任务
if get.next_exec_time not in [0, '0']:
data = {
"name": 'whole_machine_backup',
"title": '面板数据备份',
"type": "1",
"time": str(int(get.next_exec_time) + 10),
"fun": 'create_queue',
"args": {"id": get.id, "type": 1},
"model_index": "panel"
}
public.set_tasks_run(data)
else:
# 立即加入任务队列
self.create_queue(public.to_dict_obj({"id": get.id, "type": 1}))
elif int(get.type) == 2: # 还原任务
# 检查还原任务是否存在
if get.id not in self.config:
return public.returnMsg(False, '还原配置不存在,请刷新后重试!')
# 设置还原任务配置路径
reduction_task_config_path = os.path.join(self.backup_path, get.id + "_reduction.json")
# 写入还原配置文件
reduction_config = get.reduction_config
task_config = {"reduction_config": reduction_config}
public.writeFile(reduction_task_config_path, json.dumps(task_config))
# 加入还原任务队列
self.create_queue(public.to_dict_obj({"id": get.id, "type": 2}))
return public.returnMsg(True, '添加成功!')
except:
# 捕获异常,记录错误日志并返回失败消息
self.print_log(public.get_error_info())
return public.returnMsg(False, '添加任务报错了!')
def check_plugins(self, get):
import PluginLoader
"""
检查指定目录下是否存在某些插件目录,并判断对应存储是否安装
:param plugin_path: 插件目录的路径,例如 "/www/server/panel/plugin"
:return: 一个字典,包含每个插件的安装状态
"""
import os
plugin_path = "{}/plugin".format(public.get_panel_path())
# 定义需要检查的插件
plugins = ["alioss", "txcos", "ftp", "qiniu","webdav"]
# 初始化结果字典
result = {}
# 检查插件目录
for plugin in plugins:
plugin_dir = os.path.join(plugin_path, plugin)
# 初始化插件信息
result[plugin] = {
"install": os.path.exists(plugin_dir), # 插件目录是否存在
"config": False # 默认 config 为 False
}
# 检查配置状态
if result[plugin]["install"]:
try:
get.path = "/"
if plugin == "webdav":
res = PluginLoader.plugin_run(plugin, "list_files", get)
if not "status" in res:
print(res)
result[plugin]["config"] = True
else:
res = PluginLoader.plugin_run(plugin, "get_list", get)
if "list" in res and "list":
result[plugin]["config"] = True
# if not "status" in res:
# print(res)
# result[plugin]["config"] = True
except Exception as e:
result[plugin]["config"] = False
return public.returnMsg(True, result)
def delete_account(self, get):
storage_type=get.storage_type
panel_path=public.get_panel_path()
plugin_path = "{}/plugin".format(panel_path)
config_path = os.path.join(plugin_path, storage_type,"config.conf")
aes_status_path = os.path.join(plugin_path, storage_type,"aes_status")
data_path=os.path.join("{}/data".format(panel_path),"{}AS.conf".format(storage_type))
# # 检查 aes_status 文件内容
# if os.path.exists(config_path):
public.ExecShell("rm -rf {}".format(config_path))
public.ExecShell("rm -rf {}".format(aes_status_path))
public.ExecShell("rm -rf {}".format(data_path))
return public.returnMsg(True, "删除账号成功!")
def backup_download(self, get):
if not hasattr(get, 'backup_path') or not hasattr(get, 'storage_type'):
return public.returnMsg(False, '请传入backup_path!')
# 调用 check_plugins 检查云存储插件状态
plugin_status = self.check_plugins(get)["msg"]
storage_type = get.storage_type
if storage_type not in ["alioss", "txcos", "ftp", "qiniu", "webdav","local"]:
return public.returnMsg(False, "不支持该插件:{}".format(storage_type))
if storage_type in plugin_status:
if not plugin_status[storage_type]["install"]:
return public.returnMsg(False, "请先安装{}存储插件!".format(storage_type))
if not plugin_status[storage_type]["config"]:
return public.returnMsg(False, "{}存储插件未配置,请先配置!".format(storage_type))
backup_path=get.backup_path
if get.storage_type=="webdav" or get.storage_type=="local" or get.storage_type=="ftp":
if get.storage_type=="webdav":
import sys
if '/www/server/panel/plugin/webdav' not in sys.path:
sys.path.insert(0, '/www/server/panel/plugin/webdav')
try:
from webdav_main import webdav_main as webdav
# get.object_name =os.path.join(webdav().default_backup_path , "whole_machine_backup", os.path.basename(backup_path))
# self.client.download_file(from_path=download_path, to_path=local_path)
download_path=os.path.join(webdav().default_backup_path , "whole_machine_backup",os.path.basename(backup_path))
local_path = os.path.join("/tmp", os.path.basename(backup_path))
webdav().client.download_file(from_path=download_path, to_path=local_path)
path=local_path
except Exception as e:
if "could not be found in the server" in str(e):
return public.returnMsg(False, '在云存储中未发现该文件!')
return public.returnMsg(False, '请先安装webdav存储插件!')
elif get.storage_type=="ftp":
import sys
if '/www/server/panel/plugin/ftp' not in sys.path:
sys.path.insert(0, '/www/server/panel/plugin/ftp')
try:
from ftp_main import ftp_main as ftp
# get.object_name =os.path.join(webdav().default_backup_path , "whole_machine_backup", os.path.basename(backup_path))
# self.client.download_file(from_path=download_path, to_path=local_path)
ftp_backup_path=ftp().get_config(get)['backup_path']
download_path=os.path.join(ftp_backup_path , "whole_machine_backup",os.path.basename(backup_path))
local_path = os.path.join("/tmp", os.path.basename(backup_path))
ftp().client.generate_download_url(download_path)
path=local_path
except:
import traceback
print(traceback.format_exc())
return public.returnMsg(False, '请先安装ftp存储插件!')
else:
path = get.backup_path
if os.path.exists(path):
return {'status': True, 'is_loacl': True, 'path': path}
return public.returnMsg(False, '文件不存在!')
else:
name=os.path.basename(backup_path)
cloud_name=get.storage_type
import CloudStoraUpload
c = CloudStoraUpload.CloudStoraUpload()
c.run(cloud_name)
url = ''
backup_path = c.obj.backup_path
path = os.path.join(backup_path, "whole_machine_backup")
data = c.obj.get_list(path)
for i in data['list']:
print(i)
if i['name'] == name:
url = i['download']
if url == '':
return public.returnMsg(False, '在云存储中未发现该文件!')
return {'status': True, 'is_loacl': False, 'path': url}
def get_sys_backup_path_config(self,get=None):
setting_path='{}/data/whole_machine_backup_settings.json'.format(public.get_panel_path())
try:
with open(setting_path, 'r') as f:
settings = json.load(f)
except:
settings = {'sys_backup_path': self.sys_backup_path}
with open(setting_path, 'w') as f:
json.dump(settings, f)
sys_backup_path = settings.get('sys_backup_path', self.sys_backup_path)
return public.returnMsg(True,sys_backup_path)
def set_sys_backup_path_config(self, get):
setting_path='{}/data/whole_machine_backup_settings.json'.format(public.get_panel_path())
sys_backup_path = get.sys_backup_path if get.sys_backup_path.endswith('/') else get.sys_backup_path + '/'
# 检查是否是有效目录路径
if not os.path.isabs(sys_backup_path):
return public.returnMsg(False, "请输入正确的目录路径!")
settings = {
'sys_backup_path': sys_backup_path
}
with open(setting_path, 'w') as f:
json.dump(settings, f)
return public.returnMsg(True, "设置成功!")
# ======================================================================
# 开始备份
# ======================================================================
# 开始入口
def backup(self, id):
"""
备份
:param id:
:return:
"""
try:
if not isinstance(id, str):
id = id['id']
# self.lock = threading.Lock()
# self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
if id not in self.config.keys():
return public.returnMsg(False, '任务不存在!')
self.backup_save_config = self.backup_save_config.format(id)
self.backup_log_path = self.config[id]["backup_log_path"]
conf = self.config[id]
conf["status"] = -1
self.status = 1
conf["exec_time"] = time.strftime('%Y-%m-%d %H:%M:%S')
self.write_config(id, conf)
backup_path = self.backup_path
self.backup_path = conf["backup_path"] # 别用backup_path
if not os.path.exists(self.backup_path):
public.ExecShell('mkdir -p {}'.format(os.path.basename(self.backup_path)))
if not os.path.exists(os.path.join(self.backup_path, "backup")):
public.ExecShell('mkdir -p {}'.format(os.path.join(self.backup_path, "backup")))
if not os.path.exists(os.path.dirname(self.backup_log_path)):
public.ExecShell('mkdir -p {}'.format(os.path.dirname(self.backup_log_path)))
# 获取备份配置
self.backup_config = json.loads(public.readFile(conf["task_config"]))["backup_config"]
self.backup_config = json.loads(self.backup_config)
if not os.path.exists(self.backup_log_path):
public.writeFile(self.backup_log_path, json.dumps(self.backup_config))
else:
try:
self.backup_config = json.loads(public.readFile(self.backup_log_path))
except:
pass
# 创建备份目录
# 备份环境
self.backup_env(self.backup_config)
# 备份数据
self.backup_data(self.backup_config)
# 备份配置 暂时不做....
# self.backup_panel_config(self.backup_config)
# 备份完成 将备份文件压缩
conf["status"] = self.status
conf['error_num'] = self.error_num
conf['access_num'] = self.access_num
conf_path = os.path.join(conf['backup_path'], id)
public.writeFile(conf_path, json.dumps({id: conf}))
public.ExecShell('cp {} {}'.format(self.backup_log_path, conf['backup_path']))
public.ExecShell('cp {} {}'.format(os.path.join(conf['backup_path'], 'backup.json'), os.path.join(backup_path, id + '_backup.json')))
# public.ExecShell('tar -zcvf {}.tar.gz -C {} .'.format(self.backup_path, self.backup_path))
# 压缩备份文件
self.print_log(self.backup_path)
tar_gz_filename ="{}.tar.gz".format(self.backup_path)
public.ExecShell(f'tar -zcvf {tar_gz_filename} -C {self.backup_path} .')
self.print_log(f"->>>压缩备份文件完成:{tar_gz_filename}")
# 判断是否需要上传到云存储
storage_type = conf.get("storage_type", "local")
cloud_backup_path = tar_gz_filename # 初始备份路径为本地路径
if storage_type != "local" and os.path.exists(tar_gz_filename):
_cloud_name = {
"tianyiyun": "天翼云cos",
"webdav": "webdav存储",
"minio": "minio存储",
"dogecloud": "多吉云COS",
}
print(333333333333333)
if storage_type in ["tianyiyun","webdav","minio","dogecloud"]:
cloud_name_cn = _cloud_name.get(storage_type, storage_type) # 获取云存储的中文名
from CloudStoraUpload import CloudStoraUpload
_cloud_new = CloudStoraUpload()
_cloud = _cloud_new.run(storage_type)
if _cloud is False:
return False
self.print_log("->>>正在上传文件{}到{},请稍候...".format(tar_gz_filename,cloud_name_cn))
try:
backup_path = _cloud_new.backup_path
if not backup_path.endswith('/'):
backup_path += '/'
upload_path = os.path.join(backup_path, "whole_machine_backup", os.path.basename(tar_gz_filename))
self.print_log(tar_gz_filename)
self.print_log(upload_path)
if _cloud.upload_file(tar_gz_filename, upload_path):
self.print_log(f"->>>文件已成功上传到云存储:{cloud_name_cn}")
public.ExecShell('rm -rf {}'.format(tar_gz_filename))
self.print_log('删除本地文件{}'.format(tar_gz_filename))
# cloud_backup_path = upload_path + '|' + storage_type + '|' + os.path.basename(tar_gz_filename) # 更新为云存储路径
else:
self.print_log(f"->>>上传到{cloud_name_cn}失败")
except Exception as e:
self.print_log(f"->>>上传到{cloud_name_cn}时发生错误: {str(e)}")
else:
print(storage_type)
from CloudStoraUpload import CloudStoraUpload
_cloud = CloudStoraUpload()
_cloud.run(storage_type)
cloud_name_cn = _cloud.obj._title
if not _cloud.obj:
return False
self.print_log("->>>正在上传文件{}到{},请稍候...".format(tar_gz_filename,cloud_name_cn))
try:
backup_path = _cloud.obj.backup_path
if not backup_path.endswith('/'):
backup_path += '/'
upload_path = os.path.join(backup_path, "whole_machine_backup", os.path.basename(tar_gz_filename))
print(upload_path)
print((tar_gz_filename))
if _cloud.cloud_upload_file(tar_gz_filename, upload_path):
self.print_log(f"->>>已成功上传到{cloud_name_cn}")
public.ExecShell('rm -rf {}'.format(tar_gz_filename))
self.print_log('删除本地文件{}'.format(tar_gz_filename))
# cloud_backup_path = upload_path + '|' + storage_type + '|' + os.path.basename(tar_gz_filename) # 更新为云存储路径
else:
self.print_log(f"->>>上传到{cloud_name_cn}失败")
except Exception as e:
self.print_log(f"->>>上传到{cloud_name_cn}时发生错误: {str(e)}")
# return public.returnMsg(False, '备份失败!')
public.ExecShell('rm -rf {}'.format(self.backup_path))
self.write_config(id, conf)
return public.returnMsg(True, '备份完成!')
except:
print(public.get_error_info())
return public.returnMsg(False, '备份失败!')
# 修改备份状态
def change_backup_status1(self, block, name="", son_name="", status_name="", status=0, sec_name='', ):
# 通过线程锁来修改self变量并写入文件中
pass
def change_backup_status(self, block, name="", son_name="", status_name="", status=0, sec_name='', msg=''):
if status == 2:
self.status = 2
self.error_num += 1
if status == 1:
self.access_num += 1
if sec_name != '':
for i in range(len(self.backup_config[block][name][son_name])):
if self.backup_config[block][name][son_name][i]['name'] == sec_name:
self.backup_config[block][name][son_name][i][status_name] = status
if status_name == 'backup_status':
if status == 1:
self.backup_config[block][name][son_name][i]['status'] = 1
else:
self.backup_config[block][name][son_name][i]['status'] = 0
if msg != "":
self.backup_config[block][name][son_name][i]['msg'] = msg
elif son_name != "":
for i in range(len(self.backup_config[block][name])):
if self.backup_config[block][name][i]['name'] == son_name:
self.backup_config[block][name][i][status_name] = status
if status_name == 'backup_status':
if status == 1:
self.backup_config[block][name][i]['status'] = 1
else:
self.backup_config[block][name][i]['status'] = 0
if msg != "":
self.backup_config[block][name][i]['msg'] = msg
else:
self.backup_config[block][status_name] = status
if status_name == 'backup_status':
if status == 1:
self.backup_config[block]['status'] = 1
else:
self.backup_config[block]['status'] = 0
if msg != "":
self.backup_config[block]['msg'] = msg
public.writeFile(self.backup_log_path, json.dumps(self.backup_config))
def generate_md5(self, input_string):
# 创建一个 hashlib.md5 对象
md5_hash = hashlib.md5()
# 将输入的字符串编码为字节,并更新 MD5 对象
md5_hash.update(input_string.encode())
# 获取生成的 MD5 值的十六进制表示
md5_hex = md5_hash.hexdigest()
return md5_hex
# 复制一个文件或者目录到备份目录中
def copy_file(self, path):
if not os.path.exists(path):
return False
md5_name = self.generate_md5(path)
save_path = os.path.join(self.backup_path, "backup", md5_name)
if os.path.exists(save_path):
return save_path
if os.path.isdir(path):
public.ExecShell('tar -zcvf {} -C {} .'.format(save_path, path))
if os.path.isfile(path):
public.ExecShell('tar -zcvf {} -C {} {}'.format(save_path, os.path.dirname(path), os.path.basename(path)))
time.sleep(0.01)
if os.path.exists(save_path):
return save_path
else:
return False
def write_id_config(self, block, name="", son_name="", config={}, sec_name=''):
if self.id_config is None:
if not os.path.exists(self.backup_save_config):
public.writeFile(self.backup_save_config, json.dumps({}))
print(self.backup_save_config)
self.id_config = json.loads(public.readFile(self.backup_save_config))
if sec_name != "":
if block not in self.id_config:
self.id_config[block] = {}
if name not in self.id_config[block]:
self.id_config[block][name] = {}
if son_name not in self.id_config[block][name]:
self.id_config[block][name][son_name] = {}
self.id_config[block][name][son_name][sec_name] = config
elif son_name != "":
if block not in self.id_config:
self.id_config[block] = {}
if name not in self.id_config[block]:
self.id_config[block][name] = {}
self.id_config[block][name][son_name] = config
elif name != "":
if block not in self.id_config:
self.id_config[block] = {}
self.id_config[block][name] = config
else:
self.id_config[block] = config
public.writeFile(self.backup_save_config, json.dumps(self.id_config))
def uncopy_file(self, path, to_path, isfile=True, md5=False, unbak=False):
if not os.path.exists(path) and isfile:
return False
if os.path.exists(to_path):
if not unbak:
if os.path.exists(to_path + '_bak'):
public.ExecShell('rm -rf {}'.format(to_path + '_bak'))
public.ExecShell('mv {} {}'.format(to_path, to_path + '_bak'))
public.ExecShell('rm -rf {}'.format(to_path))
if isfile:
public.ExecShell('tar -zxvf {} -C {} --overwrite'.format(path, os.path.dirname(to_path)))
else:
if not isfile:
public.ExecShell('mkdir -p {}'.format(to_path))
public.ExecShell('tar -zxvf {} -C {} --overwrite'.format(path, to_path))
time.sleep(0.01)
if os.path.exists(to_path):
if md5 and isfile:
if md5 == self.get_md5(to_path):
self.print_log('文件MD5比对成功')
return True
else:
self.print_log('文件md5比对失败')
return False
return True
else:
res, i_flag = self.check_copy_error(to_path)
if res:
if isfile:
public.ExecShell('tar -zxvf {} -C {} --overwrite'.format(path, to_path))
else:
public.ExecShell('tar -zxvf {} -C {} --overwrite'.format(path, os.path.basename(to_path)))
time.sleep(0.01)
if i_flag:
public.ExecShell('chattr +i {}'.format(to_path))
if os.path.exists(to_path):
if md5 and isfile:
if md5 == self.get_md5(to_path):
self.print_log('文件MD5比对成功')
return True
else:
self.print_log('文件md5比对失败')
return False
return True
return False
def check_copy_error(self, path):
# 检查 / 目录是否满
i_flag = False
disk_status = psutil.disk_usage('/')
if disk_status.used / disk_status.total >= 0.99:
self.print_log('磁盘空间不足')
return False, i_flag
if not os.path.exists(path):
# 尝试创建目录
public.ExecShell('mkdir -p {}'.format(path))
time.sleep(0.01)
if os.path.exists(path):
return True, i_flag
# 检查目录是否有 权限写入
if not os.access(path, os.W_OK):
self.print_log('文件无法写入,尝试修复')
public.ExecShell('chmod 777 {}'.format(path))
public.ExecShell('chattr -i {}'.format(path))
if not os.access(path, os.W_OK):
self.print_log('文件无法写入,无法修复')
return False, i_flag
else:
i_flag = True, i_flag
self.print_log('文件修复成功')
return True, i_flag
return False, i_flag
# 222222222222
# ======================================================================
# 备份环境
# ======================================================================
def backup_env(self, conf):
env_conf = conf["env_list"]
connect = {
"php": self.backup_php,
"mysql": self.backup_mysql,
"mongo": self.backup_mongo,
"redis": self.backup_redis,
"pgsql": self.backup_pgsql,
"nginx": self.backup_nginx,
"apache": self.backup_apache,
"ftp": self.backup_ftp,
}
for k, v in env_conf.items():
if not isinstance(v, list):
continue
for i in v:
if i['status'] in [1, -1, 2]:
connect[k](i)
else:
if i['name'] and i["status"] != 3:
self.change_backup_status("env_list", k, i["name"], "backup_status", 4, msg="未备份".format(i["name"]))
def backup_php(self, conf):
try:
self.change_backup_status("env_list", "php", conf["name"], "backup_status", -1, msg="开始备份php{}环境".format(conf["name"]))
php_verison = conf["name"]
flag = 1
self.print_log("->>>开始获取php{}扩展列表".format(php_verison))
res = public.ExecShell("/www/server/php/{}/bin/php -m".format(php_verison))[0]
php_development = res.split("\n")
# 去待 【】和空行
if len(php_development) > 1:
php_development = php_development[1:]
php_development = [i.strip() for i in php_development if i.strip() and '[' not in i]
else:
php_development = []
# 尝试获取swoole的版本
if 'swoole' in php_development:
res = public.ExecShell("/www/server/php/{}/bin/php --ri swoole | grep Version | awk '{{print $3}}'".format(php_verison))[0]
try:
index = php_development.index('swoole')
php_development[index] = 'swoole{}'.format(res.strip()[0] if res.strip()[0] in ['4', '5'] else res.strip()[0])
except:
pass
# self.print_log(php_development)
self.print_log("获取php{}扩展列表完成".format(php_verison))
# 备份配置文件
self.print_log("开始备份php{}配置文件".format(php_verison))
php_ini = "/www/server/php/{}/etc/php.ini".format(php_verison)
res = self.copy_file(php_ini)
if not res:
self.print_log("备份php{}配置文件失败".format(php_verison))
self.change_backup_status("env_list", "php", conf["name"], "backup_status", 2, msg="备份php{}配置文件失败".format(php_verison))
flag = 2
php_ini_md5 = self.get_md5(php_ini)
php_ini_name = res
php_fpm_path = "/www/server/php/{}/etc/php-fpm.conf".format(php_verison)
res = self.copy_file(php_fpm_path)
if not res:
self.print_log("备份php{}配置文件失败".format(php_verison))
self.change_backup_status("env_list", "php", conf["name"], "backup_status", 2, msg="备份php{}配置文件失败".format(php_verison))
flag = 2
php_fpm_md5 = self.get_md5(php_fpm_path)
php_fpm_name = res
# 写入备份配置文件
self.print_log("备份php{}配置文件完成".format(php_verison))
words_path = "/www/server/panel/config/thesaurus.json"
wrods_data = None
words_data_md5 = None
if os.path.exists(words_path):
wrods_data = self.copy_file(words_path)
words_data_md5 = self.get_md5(words_path)
self.print_log("备份php{}关键词完成".format(php_verison))
config = {
"php_development": php_development,
"php_ini": php_ini_name,
"php_fpm": php_fpm_name,
"wrods_data": wrods_data,
"php_ini_md5": php_ini_md5,
"php_fpm_md5": php_fpm_md5,
"words_data_md5": words_data_md5,
}
self.write_id_config("env_list", "php", conf["name"], config)
self.change_backup_status("env_list", "php", conf["name"], "backup_status", flag, msg="备份php{}完成".format(php_verison))
self.print_log("->>>备份php{}完成".format(php_verison))
except:
self.print_log("备份php{}出错".format(php_verison))
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "php", conf["name"], "backup_status", 2, msg="备份php{}出错,程序出错".format(php_verison))
def backup_mysql(self, conf):
try:
self.print_log("->>>开始备份mysql")
flag = 1
mysql_data = None
mysql_conf = None
mysql_version = None
if not conf['setup']:
self.print_log("mysql未安装")
self.change_backup_status("env_list", "mysql", conf["name"], "backup_status", 2, msg="mysql未安装")
flag = 2
else:
self.print_log("开始备份mysql{}版本".format(conf["name"]))
mysql_version = conf["name"]
self.print_log("开始备份mysql{}配置文件".format(mysql_version))
mysql_conf = "/etc/my.cnf"
res = self.copy_file(mysql_conf)
if not res:
self.print_log("备份mysql{}配置文件失败".format(mysql_version))
flag = 2
else:
mysql_conf = res
if int(conf.get("primitive_data", 0)) == 1:
self.print_log("开始备份mysql{}数据文件".format(mysql_version))
mysql_data = public.get_mysql_info()['datadir']
res = self.copy_file(mysql_data)
if not res:
self.print_log("备份mysql{}数据文件失败".format(mysql_version))
flag = 2
else:
mysql_data = res
version = mysql_version
mysql_conf_md5 = self.get_md5("/etc/my.cnf")
config = {
"version": version,
"mysql_conf": mysql_conf,
"mysql_data": mysql_data,
"mysql_conf_md5": mysql_conf_md5,
}
self.write_id_config("env_list", "mysql", config=config)
self.change_backup_status("env_list", "mysql", son_name=conf["name"], status_name="backup_status", status=flag, msg="备份mysql完成")
self.print_log("->>>备份mysql完成")
except:
self.print_log("备份mysql出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "mysql", conf["name"], "backup_status", 2, msg="备份mysql出错,程序出错")
def backup_mongo(self, conf):
try:
self.print_log("->>> 开始备份mongo")
flag = 1
mongo_conf = None
mongo_version = None
mongo_conf_md5 = None
if not conf['setup']:
self.print_log("mongo未安装")
self.change_backup_status("env_list", "mongo", conf["name"], "backup_status", 2, msg="mongo未安装")
flag = 2
else:
self.print_log("备份mongo{}版本".format(conf["name"]))
mongo_version = conf["name"]
self.print_log("备份mongo{}配置文件".format(mongo_version))
mongo_conf = "/www/server/mongodb/config.conf"
mongo_conf_md5 = self.get_md5(mongo_conf)
res = self.copy_file(mongo_conf)
if not res:
self.print_log("备份mongo{}配置文件失败".format(mongo_version))
flag = 2
else:
mongo_conf = res
version = mongo_version
mongo_conf = mongo_conf
config = {
"version": version,
"mongo_conf": mongo_conf,
"mongo_conf_md5": mongo_conf_md5,
}
self.write_id_config("env_list", "mongo", config=config)
self.change_backup_status("env_list", "mongo", conf['name'], status_name="backup_status", status=flag, msg="备份mongo完成")
self.print_log("->>>备份mongo完成")
except:
self.print_log("备份mongo出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "mongo", conf["name"], "backup_status", 2, msg="备份mongo出错,程序出错")
def backup_redis(self, conf):
try:
self.print_log("->>> 开始备份redis")
flag = 1
redis_conf = None
redis_version = None
if not conf['setup']:
self.print_log("redis未安装")
self.change_backup_status("env_list", "redis", conf['name'], status_name="backup_status", status=2, msg="redis未安装")
flag = 2
else:
self.print_log("备份redis{}版本".format(conf["name"]))
redis_version = conf["name"]
self.print_log("备份redis{}配置文件".format(redis_version))
redis_conf = "/www/server/redis/redis.conf"
res = self.copy_file(redis_conf)
if not res:
self.print_log("备份redis{}配置文件失败".format(redis_version))
flag = 2
else:
redis_conf = res
version = redis_version
redis_conf = redis_conf
redis_conf_md5 = self.get_md5(redis_conf)
config = {
"version": version,
"redis_conf": redis_conf,
"redis_conf_md5": redis_conf_md5,
}
self.write_id_config("env_list", "redis", config=config)
self.change_backup_status("env_list", "redis", conf['name'], status_name="backup_status", status=flag, msg="备份redis完成")
self.print_log("->>>备份redis完成")
except:
self.print_log("备份redis出错")
print(public.get_error_info())
self.change_backup_status("env_list", "redis", conf['name'], status_name="backup_status", status=2, msg="备份redis出错,程序出错")
def backup_pgsql(self, conf):
try:
self.print_log("->>> 开始备份pgsql")
flag = 1
pgsql_conf = None
pgsql_version = None
client_conf = None
if not conf['setup']:
self.print_log("pgsql未安装")
self.change_backup_status("env_list", "pgsql", conf["name"], "backup_status", 2, msg="pgsql未安装")
flag = 2
else:
self.print_log("备份pgsql{}版本".format(conf["name"]))
pgsql_version = conf["name"]
self.print_log("备份pgsql{}配置文件".format(pgsql_version))
pgsql_conf = "/www/server/pgsql/data/postgresql.conf"
res = self.copy_file(pgsql_conf)
if not res:
self.print_log("备份pgsql{}主配置文件失败".format(pgsql_version))
flag = 2
else:
pgsql_conf = res
self.print_log("备份pgsql{}客户端配置文件".format(pgsql_version))
client_conf_path = "/www/server/pgsql/data/pg_hba.conf"
res = self.copy_file(client_conf_path)
if not res:
self.print_log("备份pgsql{}客户端配置文件失败".format(pgsql_version))
flag = 2
else:
client_conf = res
version = pgsql_version
pgsql_conf = pgsql_conf
client_conf = client_conf
pgsql_conf_md5 = self.get_md5(pgsql_conf)
client_conf_md5 = self.get_md5(client_conf)
config = {
"version": version,
"pgsql_conf": pgsql_conf,
"client_conf": client_conf,
"pgsql_conf_md5": pgsql_conf_md5,
"client_conf_md5": client_conf_md5,
}
self.write_id_config("env_list", "pgsql", config=config)
self.change_backup_status("env_list", "pgsql", conf['name'], status_name="backup_status", status=flag, msg="备份pgsql完成")
self.print_log("->>>备份pgsql完成")
except:
self.print_log("备份pgsql出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "pgsql", conf['name'], status_name="backup_status", status=2, msg="备份pgsql出错,程序出错")
def backup_nginx(self, conf):
try:
self.print_log("->>> 开始备份nginx")
flag = 1
nginx_conf = None
nginx_version = None
nginx_file = None
nginx_cmd = None
nginx_systemd = None
self.change_backup_status("env_list", "nginx", conf['name'], status_name="backup_status", status=-1, msg="开始备份nginx")
if not conf['setup']:
self.print_log("nginx未安装")
self.change_backup_status("env_list", "nginx", status_name="backup_status", status=2, msg="nginx未安装")
flag = 2
else:
self.print_log("开始备份nginx{}版本".format(conf["name"]))
nginx_version = conf["name"]
self.print_log("开始备份nginx{}配置文件".format(nginx_version))
nginx_conf_path = "/www/server/nginx/conf/nginx.conf"
res = self.copy_file(nginx_conf_path)
if not res:
self.print_log("备份nginx{}配置文件失败".format(nginx_version))
flag = 2
else:
nginx_conf = res
self.print_log("开始备份nginx{}文件".format(nginx_version))
nginx_file_path = "/www/server/nginx"
res = self.copy_file(nginx_file_path)
if not res:
self.print_log("备份nginx{}文件失败".format(nginx_version))
flag = 2
else:
nginx_file = res
self.print_log("开始备份nginx{}启动脚本".format(nginx_version))
nginx_cmd_path = "/etc/init.d/nginx"
res = self.copy_file(nginx_cmd_path)
if not res:
self.print_log("备份nginx{}启动脚本失败".format(nginx_version))
flag = 2
else:
nginx_cmd = res
self.print_log("开始备份nginx{}systemd服务脚本".format(nginx_version))
nginx_systemd_path = "/lib/systemd/system/nginx.service"
if os.path.exists(nginx_systemd_path):
res = self.copy_file(nginx_systemd_path)
if not res:
self.print_log("备份nginx{}systemd服务脚本失败".format(nginx_version))
flag = 2
else:
nginx_systemd = res
nginx_conf_md5 = self.get_md5(nginx_conf_path)
nginx_file_md5 = self.get_md5(nginx_file_path)
nginx_cmd_md5 = self.get_md5(nginx_cmd_path)
nginx_systemd_md5 = self.get_md5(nginx_systemd_path)
config = {
"nginx_conf": nginx_conf,
"nginx_file": nginx_file,
"nginx_cmd": nginx_cmd,
"nginx_systemd": nginx_systemd,
"version": nginx_version,
"nginx_conf_md5": nginx_conf_md5,
"nginx_file_md5": nginx_file_md5,
"nginx_cmd_md5": nginx_cmd_md5,
"nginx_systemd_md5": nginx_systemd_md5,
}
self.write_id_config("env_list", "nginx", config=config)
self.change_backup_status("env_list", "nginx", conf['name'], status_name="backup_status", status=flag, msg="备份nginx完成")
except:
self.print_log("备份nginx出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "nginx", conf['name'], status_name="backup_status", status=2, msg="备份nginx出错,程序出错")
def backup_apache(self, conf):
try:
self.print_log("->>> 开始备份apache")
self.change_backup_status("env_list", "apache", conf['name'], status_name="backup_status", status=-1, msg="开始备份apache")
flag = 1
apache_conf = None
apache_version = None
self.print_log("开始备份apache{}版本".format(conf["name"]))
version = apache_version
self.print_log("开始备份apache{}配置文件".format(version))
apache_conf_path = "/www/server/apache/conf/"
res = self.copy_file(apache_conf_path)
if not res:
self.print_log("备份apache{}配置文件失败".format(version))
flag = 2
else:
apache_conf = res
apache_conf_md5 = self.get_md5(apache_conf_path)
config = {
"version": version,
"apache_conf": apache_conf,
"apache_conf_md5": apache_conf_md5,
}
self.write_id_config("env_list", "apache", config=config)
self.change_backup_status("env_list", "apache", conf['name'], status_name="backup_status", status=flag, msg="备份apache完成")
except:
self.print_log("备份apache出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "apache", conf['name'], status_name="backup_status", status=2, msg="备份apache出错,程序出错")
def backup_ftp(self, conf):
try:
self.print_log("->>> 开始备份ftp")
self.change_backup_status("env_list", "ftp", conf['name'], status_name="backup_status", status=-1, msg="开始备份ftp")
flag = 1
version = conf["name"]
ftp_conf = None
log_status = None
if not conf['setup']:
self.print_log("ftp未安装")
self.change_backup_status("env_list", "ftp", conf["name"], "backup_status", 2, msg="ftp未安装")
flag = 2
else:
self.print_log("开始备份ftp{}配置文件".format(version))
ftp_conf_path = "/www/server/pure-ftpd/etc/pure-ftpd.conf"
res = self.copy_file(ftp_conf_path)
if not res:
self.print_log("备份ftp{}配置文件失败".format(version))
flag = 2
else:
ftp_conf = res
self.print_log("开始备份ftp{}日志状态".format(version))
rsyslog_path = "/etc/rsyslog.conf"
cofig = public.readFile(rsyslog_path)
if "ftp.*" in cofig:
log_status = 1
else:
log_status = 0
ftp_conf_md5 = self.get_md5(ftp_conf_path)
config = {
"version": version,
"ftp_conf": ftp_conf,
"log_status": log_status,
"ftp_conf_md5": ftp_conf_md5,
}
self.write_id_config("env_list", "ftp", config=config)
self.change_backup_status("env_list", "ftp", conf['name'], status_name="backup_status", status=flag, msg="备份ftp完成")
self.print_log("->>>备份ftp完成")
except:
self.print_log("备份ftp出错")
self.print_log(public.get_error_info())
self.change_backup_status("env_list", "ftp", conf['name'], status_name="backup_status", status=2, msg="备份ftp出错,程序出错")
# ======================================================================
# 备份数据
# ======================================================================
def backup_data(self, conf):
data_conf = conf["data_list"]
connect = {
"php": self.backup_php_data,
"java": self.backup_java_data,
"node": self.backup_node_data,
"python": self.backup_python_data,
"other": self.backup_other_data,
"net": self.backup_net_data,
"proxy": self.backup_proxy_data,
"html": self.backup_html_data,
"mysql": self.backup_mysql_data,
"mongodb": self.backup_mongodb_data,
"redis": self.backup_redis_data,
"pgsql": self.backup_pgsql_data,
"ftp_list": self.backup_ftp_data,
"terminal_list": self.backup_terminal_data,
"crontab_list": self.backup_crontab_data,
"port_list": self.backup_port_data,
"ip_list": self.backup_ip_data,
"port_redirect": self.backup_port_redirect_data,
"area_list": self.backup_area_data,
"ssh_config": self.ssh_config_data,
"words": self.backup_words_data,
"sqlite": self.backup_sqlite_data,
}
for k, v in data_conf.items():
if k in ['site_list', 'sql_list', 'config_list', "safety"]:
for type, dates in v.items():
if type not in connect.keys():
continue
for data in dates:
if data['status'] in [1, -1, 2]:
connect[type.lower()](data)
else:
if data['name'] and data["status"] != 3:
self.change_backup_status("data_list", k, type, "backup_status", 4, data["name"], msg="未备份".format(data["name"]))
elif k in ['ftp_list']:
for i in v:
if i['status'] in [1, -1, 2]:
connect[k](i)
else:
if i['name'] and i["status"] != 3:
self.change_backup_status("data_list", k, i["name"], "backup_status", 4, msg="未备份".format(i["name"]))
else:
for j in v:
if j['status'] in [1, -1, 2]:
connect[k](j)
else:
if j['name'] and j["status"] != 3:
self.change_backup_status("data_list", k, j["name"], "backup_status", 4, msg="未备份".format(j["name"]))
def backup_php_data(self, conf):
try:
self.print_log("->>>开始备份PHP站点:{}".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'php', "backup_status", -1, conf["name"], msg="开始备份")
# 获取网站信息
sid = conf["id"]
site_info = public.M('sites').where("id=?", (sid,)).find()
is_phpmod = False
if 'project_config' in site_info.keys():
project_config = json.loads(site_info['project_config'])
if 'type' in project_config.keys() and project_config['type'].lower() == 'phpmod':
is_phpmod = True
site_file = None
domains = None
database_info = None
redirect_list = []
redirect_nginx_data = None
redirect_apache_data = None
proxy_nginx_data = None
proxy_apache_data = None
proxy_list = []
nginx_config = None
apache_config = None
dir_auth_json = None
dir_auth_file = None
ssl_info = None
if not site_info:
self.print_log("{} 获取站点信息失败".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'php', "backup_status", 2, conf["name"], msg="获取站点信息失败")
else:
# 备份网站域名
domains = public.M('domain').where("pid=?", (sid,)).select()
if not domains or isinstance(domains, str):
self.print_log("{} 获取站点域名失败".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'php', "backup_status", 2, conf["name"], msg="获取站点域名失败")
# 备份站点文件
site_file_path = site_info["path"]
if os.path.exists(site_file_path):
site_file = self.copy_file(site_file_path)
# 备份数据库
database_info = public.M("databases").where("pid = ?", (sid,)).find()
if not database_info:
database_info = None
self.print_log("未查找到相关的数据库信息,跳过备份数据库")
elif isinstance(database_info, str):
database_info = None
self.print_log("查找数据库信息失败,跳过备份数据库")
if database_info is not None:
self.backup_mysql_data(database_info)
# 备份ftp
ftp_info = public.M("ftps").where("pid = ?", (sid,)).find()
if not ftp_info:
ftp_info = None
self.print_log("未查找到相关的ftp信息,跳过备份ftp")
elif isinstance(ftp_info, str):
ftp_info = None
self.print_log("查找ftp信息失败,跳过备份ftp")
if ftp_info is not None:
self.backup_ftp_data(ftp_info)
__redirectfile = "/www/server/panel/data/redirect.conf"
try:
redirectconf = json.loads(public.readFile(__redirectfile))
except:
redirectconf = {}
for i in redirectconf:
if i["sitename"] == conf["name"]:
redirect_list.append(i)
redirect_nginx_data_path = '/www/server/panel/vhost/nginx/redirect/{}'.format(conf["name"])
redirect_nginx_data = self.copy_file(redirect_nginx_data_path)
redirect_apache_data_path = '/www/server/panel/vhost/apache/redirect/{}'.format(conf["name"])
redirect_apache_data = self.copy_file(redirect_apache_data_path)
if not is_phpmod:
__proxyfile = '{}/data/proxyfile.json'.format(public.get_panel_path())
else:
__proxyfile = "{}/data/mod_proxy_file.conf".format(public.get_panel_path())
try:
proxyUrl = json.loads(public.readFile(__proxyfile))
except:
proxyUrl = {}
for i in proxyUrl:
if i["sitename"] == conf["name"]:
proxy_list.append(i)
proxy_nginx_data_path = '/www/server/panel/vhost/nginx/proxy/{}'.format(conf["name"])
proxy_nginx_data = self.copy_file(proxy_nginx_data_path)
proxy_apache_data_path = '/www/server/panel/vhost/apache/proxy/{}'.format(conf["name"])
proxy_apache_data = self.copy_file(proxy_apache_data_path)
import panelSite
# 备份网站目录
site_run_path = panelSite.panelSite().GetDirUserINI(public.to_dict_obj({'path': site_info['path'], 'id': site_info['id']}))
import config
# 备份ssl配置
ssl_path = "/www/server/panel/vhost/cert/{}".format(site_info["name"])
if os.path.exists(ssl_path):
ssl_info = self.copy_file(ssl_path)
# 备份web配置文件
nginx_config_path = "/www/server/panel/vhost/nginx/{}.conf".format(site_info["name"])
apache_config_path = "/www/server/panel/vhost/apache/{}.conf".format(site_info["name"])
if os.path.exists(nginx_config_path):
nginx_config = self.copy_file(nginx_config_path)
if os.path.exists(apache_config_path):
apache_config = self.copy_file(apache_config_path)
# 备份目录限制
dir_auth_json_path = "/www/server/panel/data/site_dir_auth.json"
if os.path.exists(dir_auth_json_path):
dir_auth_json = self.copy_file(dir_auth_json_path)
dir_auth_file_path = "/www/server/panel/vhost/nginx/dir_auth/{}".format(site_info["name"])
if os.path.exists(dir_auth_file_path):
dir_auth_file = self.copy_file(dir_auth_file_path)
banding = public.M('binding').where('pid=?', (site_info['id'],)).field('id,pid,domain,path,port,addtime').select()
site_file_md5 = self.get_md5(site_file_path)
nginx_config_md5 = self.get_md5(nginx_config_path)
apache_config_md5 = self.get_md5(apache_config_path)
dir_auth_json_md5 = self.get_md5(dir_auth_json_path)
dir_auth_file_md5 = self.get_md5(dir_auth_file_path)
# 备份伪静态数据
pseudo_static_data_path = "/www/server/panel/vhost/rewrite/{}.conf".format(site_info["name"])
pseudo_static_data_md5 = self.get_md5(pseudo_static_data_path)
pseudo_static_data = self.copy_file(pseudo_static_data_path)
php_version = panelSite.panelSite().GetSitePHPVersion(public.to_dict_obj({'siteName': site_info['name']}))['phpversion']
site_info["php_version"] = php_version
config = {
"site_file": site_file,
"domains": domains,
"database_info": database_info,
"redirect_list": redirect_list,
"proxy_list": proxy_list,
"site_run_path": site_run_path,
"ssl_info": ssl_info,
"nginx_config": nginx_config,
"apache_config": apache_config,
"dir_auth_file": dir_auth_file,
"dir_auth_json": dir_auth_json,
"banding": banding,
"site_file_md5": site_file_md5,
"nginx_config_md5": nginx_config_md5,
"apache_config_md5": apache_config_md5,
"dir_auth_json_md5": dir_auth_json_md5,
"dir_auth_file_md5": dir_auth_file_md5,
"site_info": site_info,
"ftp_info": ftp_info,
"pseudo_static_data": pseudo_static_data,
"pseudo_static_data_md5": pseudo_static_data_md5,
"redirect_nginx_data": redirect_nginx_data,
"redirect_apache_data": redirect_apache_data,
"proxy_nginx_data": proxy_nginx_data,
"proxy_apache_data": proxy_apache_data,
}
self.print_log(config)
self.write_id_config("data_list", "site_list", "php", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "site_list", 'php', "backup_status", 1, conf["name"], msg="备份PHP站点:{}完成".format(conf["name"]))
self.print_log("->>>备份PHP站点:{}完成".format(conf["name"]))
except:
self.change_backup_status("data_list", "site_list", 'php', "backup_status", 2, conf["name"], msg="备份PHP站点:{}出错,程序出错".format(conf["name"]))
self.print_log("备份PHP站点:{}出错".format(conf["name"]))
self.print_log(public.get_error_info())
def backup_java_data(self, conf):
self.print_log("->>>备份Java站点:{},跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'java', "backup_status", 2, conf["name"])
return
self.print_log("->>>开始备份Java站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份Java站点:{}完成".format(conf["name"]))
def backup_node_data(self, conf):
self.print_log("->>>备份Node站点:{}跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'node', "backup_status", 2, conf["name"], msg="备份Node站点:{}跳过".format(conf["name"]))
return
self.print_log("->>>开始备份Node站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份Node站点:{}完成".format(conf["name"]))
def backup_python_data(self, conf):
self.print_log("->>>备份Python站点:{}跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'python', "backup_status", 2, conf["name"], msg="备份Python站点:{}跳过".format(conf["name"]))
return
self.print_log("->>>开始备份Python站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份Python站点:{}完成".format(conf["name"]))
def backup_other_data(self, conf):
self.print_log("->>>备份其他站点:{}跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'other', "backup_status", 2, conf["name"], msg="备份其他站点:{}跳过".format(conf["name"]))
return
self.print_log("->>>开始备份其他站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份其他站点:{}完成".format(conf["name"]))
def backup_net_data(self, conf):
self.print_log("->>>备份.NET站点:{}跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'net', "backup_status", 2, conf["name"], msg="备份.NET站点:{}跳过".format(conf["name"]))
return
self.print_log("->>>开始备份.NET站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份.NET站点:{}完成".format(conf["name"]))
def backup_proxy_data(self, conf):
self.print_log("->>>开始备份代理站点:{}".format(conf["name"]))
nginx_config = None
apache_config = None
site_info = None
site_info = public.M('sites').where("name=?", (conf["name"],)).select()
self.print_log("数据库信息备份成功")
proxy_config_path = "/www/server/proxy_project/sites/{}".format(conf["name"])
if not os.path.exists(proxy_config_path):
self.print_log("代理站点:{},配置文件不存在,备份失败".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'proxy', "backup_status", 2, conf["name"], msg="代理站点:{},配置文件不存在,备份失败".format(conf["name"]))
return
proxy_config = self.copy_file(proxy_config_path)
self.print_log("代理站点:{}配置文件备份成功".format(conf["name"]))
nginx_config_path = "/www/server/panel/vhost/nginx/{}.conf".format(conf["name"])
if os.path.exists(nginx_config_path):
nginx_config = self.copy_file(nginx_config_path)
apache_config_path = "/www/server/panel/vhost/apache/{}.conf".format(conf["name"])
if os.path.exists(apache_config_path):
apache_config = self.copy_file(apache_config_path)
self.print_log("代理站点:{}web配置文件备份成功".format(conf["name"]))
proxy_config_md5 = self.get_md5(proxy_config_path)
nginx_config_md5 = self.get_md5(nginx_config_path)
apache_config_md5 = self.get_md5(apache_config_path)
config = {
"proxy_config": proxy_config,
"nginx_config": nginx_config,
"apache_config": apache_config,
"site_info": site_info,
"proxy_config_md5": proxy_config_md5,
"nginx_config_md5": nginx_config_md5,
"apache_config_md5": apache_config_md5,
}
self.write_id_config("data_list", "site_list", "proxy", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "site_list", 'proxy', "backup_status", 1, conf["name"], msg="备份代理站点:{}完成".format(conf["name"]))
self.print_log("->>>备份代理站点:{}完成".format(conf["name"]))
def backup_html_data(self, conf):
self.print_log("->>>备份静态站点:{}跳过".format(conf["name"]))
self.change_backup_status("data_list", "site_list", 'html', "backup_status", 2, conf["name"], msg="备份静态站点:{}跳过".format(conf["name"]))
return
self.print_log("->>>开始备份静态站点:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>备份静态站点:{}完成".format(conf["name"]))
# 检查mysql的备份文件是否正常
def check_mysql_files(self, sql_path, sql_name):
if not os.path.exists(sql_path):
self.print_log("备份mysql数据库:{}失败,备份文件不存在!".format(sql_name))
return False
if os.path.isdir(sql_path):
self.print_log("备份mysql数据库:{}失败,备份文件为目录!".format(sql_name))
return False
# 获取sql文件数据库大小
sql_size = os.path.getsize(sql_path)
if sql_size > 10 * 1024 * 1024:
self.print_log("备份mysql数据库:{},备份文件大于10M,跳过数据校验!".format(sql_name))
return True
# 验证备份是否正常结束
data = public.GetNumLines(sql_path, 3)
if data.find("Dump completed on") == -1:
self.print_log("备份mysql数据库:{}失败,备份非正常结束!".format(sql_name))
return False
# 验证所有表是否都在sql文件中
# 获取数据库的所有表名称
table_list = database.database().GetInfo(public.to_dict_obj({'db_name': sql_name}))
table_list = [i['table_name'] for i in table_list["tables"]]
if not table_list:
self.print_log("备份mysql数据库:{}数据校验成功,数据库无表".format(sql_name))
return True
self.print_log(table_list)
res = public.ExecShell("grep -o -F -e {} {}".format(" -e ".join(table_list), sql_path))[0]
# 去重和去空
res = list(set(res.split("\n")))
res = [i.strip() for i in res if i.strip()]
if len(res) != len(table_list):
self.print_log("备份mysql数据库:{}失败,备份文件不完整!".format(sql_name))
return False
self.print_log("备份mysql数据库:{}数据校验成功".format(sql_name))
return True
def backup_mysql_data(self, conf):
try:
self.print_log("->>>开始备份mysql数据库:{}".format(conf["name"]))
self.print_log(conf)
sql_data_md5 = None
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", -1, conf["name"], msg="开始备份mysql数据库:{}".format(conf["name"]))
mysql_info = public.M("databases").where("name=?", (conf['name'],)).find()
if not mysql_info or isinstance(mysql_info, str):
self.print_log("获取mysql数据库信息失败")
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 2, conf["name"], msg="获取mysql数据库信息失败")
return
# 远程数据库只备份信息
if mysql_info["db_type"] == 2:
config = {
"mysql_info": mysql_info,
"cloud_server": public.M('database_servers').where('id=?', (mysql_info['sid'])).select()
}
if not config["cloud_server"]:
self.print_log("获取远程数据库信息失败")
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 2, conf["name"], msg="获取远程数据库信息失败")
return
else:
config["cloud_server"] = config["cloud_server"][0]
self.write_id_config("data_list", "sql_list", "mysql", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 1, conf["name"])
self.print_log("->>>备份mysql数据库:{}完成,远程数据库".format(conf["name"]))
return
self.print_log("32")
sql_path = '/tmp/{}.sql'.format(conf["name"])
md5_name = self.generate_md5(sql_path)
save_path = os.path.join(self.backup_path, "backup", md5_name)
sql_data = save_path
if not os.path.exists(save_path):
if not os.path.exists(self.backup_path):
self.print_log("333333333332")
os.makedirs(self.backup_path)
_MYSQLDUMP_BIN = public.get_mysqldump_bin()
db_password = public.M("config").where("id=?", (1,)).getField("mysql_root")
self.print_log("33333333333333333333332")
try:
db_port = int(panelMysql.panelMysql().query("show global variables like 'port'")[0][1])
except:
db_port = 3306
mysql_obj = db_mysql.panelMysql()
flag = mysql_obj.set_host('localhost', db_port, None, 'root', db_password)
if flag is False:
self.print_log("连接mysql数据库失败")
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 2, conf["name"], msg="连接mysql数据库失败")
return
# 开始备份数据库
resp = public.ExecShell("{} --help | grep set-gtid-purged >> /tmp/backup_sql.log".format(_MYSQLDUMP_BIN))[0]
set_gtid_purged = ''
if resp.find("--set-gtid-purged") != -1:
set_gtid_purged = "--set-gtid-purged=OFF"
shell = "'{mysqldump_bin}' {set_gtid_purged} --opt --skip-lock-tables --single-transaction --routines --events --skip-triggers --default-character-set='{db_charset}' --force " \
"--host='{db_host}' --port={db_port} --user='{db_user}' --password='{db_password}' '{db_name}' > {sql_path}".format(
mysqldump_bin=_MYSQLDUMP_BIN,
set_gtid_purged=set_gtid_purged,
db_charset='utf8mb4',
db_host='localhost',
db_port=db_port,
db_user='root',
db_password=db_password,
db_name=conf["name"],
sql_path=sql_path
)
public.ExecShell(shell)
if not self.check_mysql_files(sql_path, conf["name"]):
self.print_log("3333333333333333333333333333333")
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 2, conf["name"], msg="备份mysql数据库:{}失败".format(conf["name"]))
self.print_log("333333333333333333333333333333333333333333333333333")
return
time.sleep(0.1)
if os.path.exists(sql_path):
# 转移文件
sql_data_md5 = self.get_md5(sql_path)
sql_data = self.copy_file(sql_path)
self.print_log("备份mysql数据库:{}数据打包成功!".format(conf["name"]))
public.ExecShell('rm -rf {}'.format(sql_path))
# 备份数据库的权限
sql_power = database.database().GetDatabaseAccess(public.to_dict_obj({'name': conf["name"]}))['msg']
sql_info = database.database().GetInfo(public.to_dict_obj({'db_name': conf["name"]}))
mysql_version = public.readFile('/www/server/mysql/version.pl')
config = {
"sql_data": sql_data,
"mysql_info": mysql_info,
"sql_power": sql_power,
"sql_info": sql_info,
"mysql_version": mysql_version,
"sql_data_md5": sql_data_md5,
}
self.print_log(config)
self.print_log("3333333333333333333")
self.write_id_config("data_list", "sql_list", "mysql", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'mysql', "backup_status", 1, conf["name"], msg="备份mysql数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份mysql数据库:{}完成".format(conf["name"]))
except:
print(public.get_error_info())
def backup_mongodb_data(self, conf):
self.print_log("->>>开始备份mongo数据库:{}".format(conf["name"]))
mongo_info = public.M("databases").where("name=? AND LOWER(type)=LOWER('mongodb')", (conf['name'],)).find()
if not mongo_info or isinstance(mongo_info, str):
self.print_log("获取mongo数据库信息失败")
self.change_backup_status("data_list", "sql_list", 'mongodb', "backup_status", 2, conf["name"], msg="获取mongo数据库信息失败")
return
# 远程数据库只备份信息
if mongo_info["db_type"] == 2:
config = {
"mongo_info": mongo_info,
"cloud_server": public.M('database_servers').where('db_type=?', ('mongodb')).select()
}
self.write_id_config("data_list", "sql_list", "mongodb", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'mongodb', "backup_status", 1, conf["name"], msg="备份mongo数据库:{}完成".format(conf["name"]))
return
# 备份数据库
mongo_path = '/tmp/{}'.format(conf["name"])
md5_name = self.generate_md5(mongo_path)
save_path = os.path.join(self.backup_path, "backup", md5_name)
mongo_data = save_path
mongo_data_md5 = None
if not os.path.exists(save_path):
_MONGODBDUMP_BIN = os.path.join(public.get_setup_path(), "mongodb/bin/mongodump")
_MONGOEXPORT_BIN = os.path.join(public.get_setup_path(), "mongodb/bin/mongoexport")
db_password = mongo_info["password"]
from databaseModel.mongodbModel import panelMongoDB
if mongo_info["db_type"] == 0:
if panelMongoDB().get_config_options("security", "authorization", "disabled") == "enabled":
if not db_password:
return public.returnMsg(False, "数据库密码为空!请先设置数据库密码!")
else:
db_password = None
db_port = panelMongoDB.get_config_options("net", "port", 27017)
db_host = "127.0.0.1"
db_user = mongo_info["username"]
mongodump_shell = "'{mongodump_bin}' --host='{db_host}' --port={db_port} --db='{db_name}' --out='{out}'".format(
mongodump_bin=_MONGODBDUMP_BIN,
db_host=db_host,
db_port=int(db_port),
db_name=conf["name"],
out=mongo_path,
)
if db_password: # 本地未开启安全认证
mongodump_shell += " --username='{db_user}' --password={db_password}".format(db_user=db_user, db_password=public.shell_quote(str(db_password)))
public.ExecShell('{}'.format(mongodump_shell))
time.sleep(0.1)
if os.path.exists(mongo_path):
self.print_log("备份mongo数据库:{}完成".format(conf["name"]))
# 转移文件
mongo_data = self.copy_file(mongo_path)
mongo_data_md5 = self.get_md5(mongo_path)
public.ExecShell('rm -rf {}'.format(mongo_path))
from databaseModel.mongodbModel import main
sql_power = main().GetDatabaseAccess(public.to_dict_obj({'user_name': mongo_info["username"]}))['data']
config = {
"mongo_data": mongo_data,
"mongo_info": mongo_info,
"sql_power": sql_power,
"mongo_data_md5": mongo_data_md5
}
self.print_log(config)
self.write_id_config("data_list", "sql_list", "mongodb", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'mongodb', "backup_status", 1, conf["name"], msg="备份mongo数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份mongo数据库:{}完成".format(conf["name"]))
def backup_redis_data(self, conf):
self.print_log("->>>开始备份redis数据库:{}".format(conf["name"]))
from databaseModel.redisModel import panelRedisDB
redis_db = panelRedisDB().set_host(decode_responses=False)
status, redis_obj = redis_db.connect(0)
if status is False:
self.print_log("连接redis数据库失败")
self.change_backup_status("data_list", "sql_list", 'redis', "backup_status", 2, conf["name"], msg="连接redis数据库失败")
return
redis_obj.bgsave()
# 等待后台保存操作完成
while True:
# 获取持久化信息
progress_info = redis_obj.info("persistence")
bgsave_in_progress = int(progress_info.get("rdb_bgsave_in_progress", 0))
if bgsave_in_progress == 0:
break
time.sleep(1) # 等待1秒后再次检查
dump_path = os.path.join(redis_obj.config_get("dir")["dir"], redis_obj.config_get("dbfilename")["dbfilename"])
if not os.path.exists(dump_path):
self.print_log("备份redis数据库:{}失败".format(conf["name"]))
self.change_backup_status("data_list", "sql_list", 'redis', "backup_status", 2, conf["name"], msg="备份redis数据库:{}失败".format(conf["name"]))
return
redis_data = self.copy_file(dump_path)
redis_data_md5 = self.get_md5(dump_path)
public.ExecShell('rm -rf {}'.format(dump_path))
config = {
"redis_data": redis_data,
"redis_info": conf,
"redis_data_md5": redis_data_md5
}
self.print_log(config)
self.write_id_config("data_list", "sql_list", "redis", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'redis', "backup_status", 1, conf["name"], msg="备份redis数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份redis数据库:{}完成".format(conf["name"]))
def backup_pgsql_data(self, conf):
self.print_log("->>>开始备份pgsql数据库:{}".format(conf["name"]))
pgsql_info = public.M("databases").where("name=? AND LOWER(type)=LOWER('pgsql')", (conf['name'],)).find()
self.print_log(pgsql_info)
pgsql_data = None
if not pgsql_info or isinstance(pgsql_info, str):
self.print_log("获取pgsql数据库信息失败")
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 2, conf["name"], msg="获取pgsql数据库信息失败")
return
# 远程数据库只备份信息
if pgsql_info["db_type"] == 2:
config = {
"pgsql_info": pgsql_info,
"cloud_server": public.M('database_servers').where('db_type=?', ('pgsql')).select()
}
self.write_id_config("data_list", "sql_list", "pgsql", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 1, conf["name"], msg="备份pgsql数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份pgsql数据库:{}完成".format(conf["name"]))
return
# 备份数据库
db_name = pgsql_info["name"]
pgsql_data_md5 = None
db_user = "postgres"
db_host = "127.0.0.1"
from databaseModel.pgsqlModel import panelPgsql
db_port = panelPgsql().get_config_options("port", int, 5432)
t_path = os.path.join(public.get_panel_path(), "data/postgresAS.json")
if not os.path.isfile(t_path):
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 2, conf["name"], msg="获取数据库密码失败")
self.print_log("获取数据库密码失败")
db_password = ""
if os.path.exists(t_path):
try:
db_password = json.loads(public.readFile(t_path)).get("password", "")
except:
db_password = ""
if not db_password:
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 2, conf["name"], msg="获取数据库密码失败")
self.print_log("获取数据库密码失败")
return
pgsql_obj = panelPgsql().set_host(host=db_host, port=db_port, database=None, user=db_user, password=db_password)
status, err_msg = pgsql_obj.connect()
if status is False:
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 2, conf["name"], msg="连接数据库失败[{}:{}]".format(db_host, db_port))
self.print_log("连接数据库失败[{}:{}]".format(db_host, db_port))
return
_PGDUMP_BIN = os.path.join(public.get_setup_path(), "pgsql/bin/pg_dump")
backup_path = '/tmp/{}.sql'.format(db_name)
md5_name = self.generate_md5(backup_path)
pgsql_data = os.path.join(self.backup_path, "backup", md5_name)
if not os.path.exists(backup_path):
shell = "'{pgdump_bin}' --host='{db_host}' --port={db_port} --username='{db_user}' --dbname='{db_name}' --clean > {backup_path}".format(
pgdump_bin=_PGDUMP_BIN,
db_host=db_host,
db_port=int(db_port),
db_user=db_user,
db_name=db_name,
backup_path=backup_path
)
public.ExecShell(shell)
time.sleep(0.1)
if os.path.exists(backup_path):
self.print_log("备份pgsql数据库:{}完成".format(conf["name"]))
# 转移文件
pgsql_data = self.copy_file(backup_path)
pgsql_data_md5 = self.get_md5(backup_path)
public.ExecShell('rm -rf {}'.format(backup_path))
config = {
"pgsql_data": pgsql_data,
"pgsql_info": pgsql_info,
"pgsql_data_md5": pgsql_data_md5
}
self.print_log(config)
self.write_id_config("data_list", "sql_list", "pgsql", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'pgsql', "backup_status", 1, conf["name"], msg="备份pgsql数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份pgsql数据库:{}完成".format(conf["name"]))
def backup_sqlServer_data(self, conf):
self.print_log("->>>开始备份sqlServer数据库:{}".format(conf["name"]))
sql_info = public.M("databases").where("name=? AND LOWER(type)=LOWER('sqlserver')", (conf['name'],)).find()
if not sql_info or isinstance(sql_info, str):
self.print_log("获取sqlServer数据库信息失败")
self.change_backup_status("data_list", "sql_list", 'sqlserver', "backup_status", 2, conf["name"], msg="获取sqlServer数据库信息失败")
return
# 远程数据库只备份信息
config = {
"sql_info": sql_info,
"cloud_server": public.M('database_servers').where('db_type=?', ('sqlserver')).select()
}
self.write_id_config("data_list", "sql_list", "sqlserver", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", 'sqlserver', "backup_status", 1, conf["name"], msg="备份sqlServer数据库:{}完成".format(conf["name"]))
self.print_log("->>>备份sqlServer数据库:{}完成".format(conf["name"]))
return
def backup_ftp_data(self, conf):
self.print_log("->>>开始备份ftp:{}".format(conf["name"]))
self.print_log(conf)
ftp_info = public.M("ftps").where("id=?", (conf["id"],)).find()
if not ftp_info or isinstance(ftp_info, str):
self.print_log("获取ftp信息失败")
self.change_backup_status("data_list", "ftp_list", "ftp", "backup_status", 2, conf["name"], msg="获取ftp信息失败")
return
ftp_data = None
if os.path.exists(ftp_info["path"]):
ftp_data = self.copy_file(ftp_info["path"])
ftp_data_md5 = self.get_md5(ftp_info["path"])
config = {
"ftp_data": ftp_data,
"ftp_info": ftp_info,
"ftp_data_md5": ftp_data_md5
}
self.print_log(config)
self.write_id_config("data_list", "ftp_list", conf["name"], config=config)
self.change_backup_status("data_list", "ftp_list", conf['name'], "backup_status", 1, msg="备份ftp:{}完成".format(conf["name"]))
self.print_log("->>>备份ftp:{}完成".format(conf["name"]))
def backup_terminal_data(self, conf):
self.print_log("->>>开始备份服务器列表,常用命令")
self.print_log(conf)
terminal_data = None
terminal_data_path = '/www/server/panel/config/ssh_info'
if os.path.exists(terminal_data_path):
terminal_data = self.copy_file(terminal_data_path)
terminal_data_md5 = self.get_md5(terminal_data_path)
config = {
"terminal_data": terminal_data,
"terminal_data_md5": terminal_data_md5,
}
self.write_id_config("data_list", "terminal_list", "服务器列表", config=config)
self.write_id_config("data_list", "terminal_list", "常用命令", config=config)
self.change_backup_status("data_list", "terminal_list", "服务器列表", "backup_status", 1, msg="备份服务器列表完成")
self.change_backup_status("data_list", "terminal_list", "常用命令", "backup_status", 1, msg="备份常用命令完成")
self.print_log("->>>备份服务器列表,常用命令完成")
def backup_crontab_data(self, conf):
self.print_log("->>>开始备份计划任务:{}".format(conf["name"]))
crontab_list = public.M("crontab").select()
self.write_id_config("data_list", "crontab_list", "crontab", config=crontab_list)
self.change_backup_status("data_list", "crontab_list", "计划任务列表", "backup_status", 1, msg="备份计划任务完成")
self.print_log("->>>备份计划任务:{}完成".format(conf["name"]))
def backup_port_data(self, conf):
self.print_log("->>>开始备份端口:{}".format(conf["name"]))
self.print_log(conf)
from firewallModel.comModel import main as com
port_data = None
port_data_md5 = None
try:
port_data_path = com().export_rules(public.to_dict_obj({"data": {'rule_name': 'port_rule'}}))['msg']
port_data = self.copy_file(port_data_path)
port_data_md5 = self.get_md5(port_data_path)
except:
self.change_backup_status("data_list", "safety", "port_list", "backup_status", 2, conf["name"], msg="备份端口:{}出错,程序出错".format(conf["name"]))
self.print_log("备份端口:{}出错".format(conf["name"]))
return
config = {
"port_data": port_data,
"port_data_md5": port_data_md5,
"port_data_path": port_data_path
}
self.print_log(config)
self.write_id_config("data_list", "safety", "port_list", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "port_list", "backup_status", 1, conf["name"], msg="备份端口:{}完成".format(conf["name"]))
self.print_log("->>>备份端口:{}完成".format(conf["name"]))
def backup_ip_data(self, conf):
self.print_log("->>>开始备份IP:{}".format(conf["name"]))
from firewallModel.comModel import main as com
ip_data = None
ip_data_md5 = None
try:
ip_list_path = com().export_rules(public.to_dict_obj({"rule": 'ip', 'chain': 'ALL'}))['msg']
ip_data = self.copy_file(ip_list_path)
ip_data_md5 = self.get_md5(ip_list_path)
except:
self.change_backup_status("data_list", "safety", "ip_list", "backup_status", 2, conf["name"], msg="备份IP:{}出错,程序出错".format(conf["name"]))
print(public.get_error_info())
self.print_log("备份IP:{}出错".format(conf["name"]))
return
config = {
"ip_data": ip_data,
"ip_data_md5": ip_data_md5,
"ip_list_path": ip_list_path
}
self.write_id_config("data_list", "safety", "ip_list", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "ip_list", "backup_status", 1, conf["name"], msg="备份IP:{}完成".format(conf["name"]))
self.print_log(config)
self.print_log("->>>备份IP:{}完成".format(conf["name"]))
def backup_port_redirect_data(self, conf):
self.print_log("->>>开始备份端口转发:{}".format(conf["name"]))
from firewallModel.comModel import main as com
forward = None
forward_md5 = None
try:
forward_path = com().export_rules(public.to_dict_obj({"rule": 'forward'}))['msg']
forward = self.copy_file(forward_path)
forward_md5 = self.get_md5(forward_path)
except:
self.change_backup_status("data_list", "safety", "ip_list", "port_redirect", 2, conf["name"], msg="备份IP:{}出错,程序出错".format(conf["name"]))
print(public.get_error_info())
self.print_log("备份IP:{}出错".format(conf["name"]))
return
config = {
"forward": forward,
"forward_md5": forward_md5,
"forward_path": forward_path
}
self.write_id_config("data_list", "safety", "port_redirect", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "port_redirect", "backup_status", 1, conf["name"], msg="备份端口转发:{}完成".format(conf["name"]))
self.print_log(config)
self.print_log("->>>备份端口转发:{}完成".format(conf["name"]))
def backup_area_data(self, conf):
self.print_log("->>>开始备份区域:{}".format(conf["name"]))
area_data = None
area_data_md5 = None
try:
area_data_path = safe_firewall_main().export_rules(public.to_dict_obj({'rule_name': 'country_rule'}))['msg']
area_data = self.copy_file(area_data_path)
area_data_md5 = self.get_md5(area_data_path)
except:
self.change_backup_status("data_list", "safety", "area_list", "backup_status", 2, conf["name"], msg="备份区域:{}出错,程序出错".format(conf["name"]))
self.print_log("备份区域:{}出错".format(conf["name"]))
return
config = {
"area_data": area_data,
"area_data_md5": area_data_md5,
"area_data_path": area_data_path
}
self.write_id_config("data_list", "safety", "area_list", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "area_list", "backup_status", 1, conf["name"], msg="备份区域:{}完成".format(conf["name"]))
self.print_log("->>>备份区域:{}完成".format(conf["name"]))
def ssh_config_data(self, conf):
self.print_log("->>>开始备份SSH配置:{}".format(conf["name"]))
ssh_data = None
ssh_data_md5 = None
ssh_config_path = '/etc/ssh/sshd_config'
if os.path.exists(ssh_config_path):
ssh_data = self.copy_file(ssh_config_path)
ssh_data_md5 = self.get_md5(ssh_config_path)
else:
self.print_log("未找到SSH配置文件")
self.change_backup_status("data_list", "safety", "ssh_config", "backup_status", 2, conf["name"], msg="未找到SSH配置文件")
return
config = {
"ssh_data": ssh_data,
"ssh_data_md5": ssh_data_md5
}
self.write_id_config("data_list", "safety", "ssh_config", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "ssh_config", "backup_status", 1, conf["name"], msg="备份SSH配置:{}完成".format(conf["name"]))
self.print_log(config)
self.print_log("->>>备份SSH配置:{}完成".format(conf["name"]))
def backup_words_data(self, conf):
self.print_log("->>>开始备份关键词:{}".format(conf["name"]))
words_path = "/www/server/panel/config/thesaurus.json"
wrods_data = None
wrod_data_md5 = None
if os.path.exists(words_path):
wrods_data = self.copy_file(words_path)
wrod_data_md5 = self.get_md5(words_path)
config = {
"wrods_data": wrods_data,
"wrod_data_md5": wrod_data_md5
}
self.write_id_config("data_list", "safety", "words", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "safety", "words", "backup_status", 1, conf["name"], msg="备份关键词:{}完成".format(conf["name"]))
self.print_log(config)
self.print_log("->>>备份关键词:{}完成".format(conf["name"]))
def backup_sqlite_data(self, conf):
self.print_log("->>>开始备份sqlite:{}".format(conf["name"]))
sqlite_path = conf["name"]
sqlite_data = None
sqlite_data_md5 = None
if os.path.exists(sqlite_path):
sqlite_data = self.copy_file(sqlite_path)
sqlite_data_md5 = self.get_md5(sqlite_path)
config = {
"sqlite_data": sqlite_data,
"sqlite_info": conf["name"],
"sqlite_data_md5": sqlite_data_md5
}
self.write_id_config("data_list", "sql_list", "sqlite", config=config, sec_name=conf["name"])
self.change_backup_status("data_list", "sql_list", "sqlite", "backup_status", 1, conf["name"], msg="备份sqlite:{}完成".format(conf["name"]))
self.print_log(config)
self.print_log("->>>备份sqlite:{}完成".format(conf["name"]))
# ======================================================================
# 恢复数据
# ======================================================================
# 获取备份记录
def get_backup_record(self, id):
"""
获取备份记录
:param id:
:return:
"""
if id not in self.config:
return public.returnMsg(False, '任务不存在!')
if self.backup_record is None:
backup_log_path = self.config[id]["backup_log_path"]
res = json.loads(public.readFile(backup_log_path))
self.backup_record = res
# 恢复入口函数
def reduction(self, id):
"""
还原
:param id:
:return:
"""
if not isinstance(id, str):
id = id["id"]
conf = self.config[id]
self.get_backup_record(id)
reduction = json.loads(public.readFile(conf["reduction_task_config_path"]))["reduction_config"]
if not reduction:
self.print_log("还原信息不存在")
return public.returnMsg(False, '还原配置不存在')
if isinstance(reduction, str):
reduction = json.loads(reduction)
self.reduction_config = reduction
self.reduction_log_path = conf["reduction_log_path"]
# 取备份中存储的数据
try:
backup_data_path = os.path.join(self.backup_path, id + "_backup.json")
self.backup_task_data = json.loads(public.readFile(backup_data_path))
except:
pass
if not self.backup_task_data:
self.print_log("备份信息检查失败,恢复失败")
return public.returnMsg(False, '备份信息检查失败,恢复失败')
conf["status"] = 3
self.status = 4
conf["start_time"] = time.strftime('%Y-%m-%d %X')
self.write_config(id, conf)
# 开始解压备份文件
try:
if not os.path.exists(conf['backup_path'] + '.tar.gz'):
self.print_log("备份文件不存在")
return public.returnMsg(False, '备份文件不存在')
public.ExecShell('mkdir -p {}'.format(conf['backup_path']))
public.ExecShell('tar -xzf {} -C {}'.format(conf['backup_path'] + '.tar.gz', conf['backup_path']))
except:
pass
# 还原环境
self.reduction_env(self.reduction_config)
# 还原数据
self.reduction_data(self.reduction_config)
# 还原配置
# self.reduction_panel_config(self.reduction_config)
conf["status"] = self.status
conf['access_num'] = self.access_num
conf['error_num'] = self.error_num
self.write_config(id, conf)
return public.returnMsg(True, '还原结束')
# 写恢复进度日志
def change_reduction_status(self, block, name="", son_name="", status_name="", status=0, sec_name='', msg=''):
if status == 2:
self.status = 5
self.error_num += 1
if status == 1:
self.access_num += 1
if sec_name != '':
for i in range(len(self.reduction_config[block][name][son_name])):
if self.reduction_config[block][name][son_name][i]['name'] == sec_name:
self.reduction_config[block][name][son_name][i][status_name] = status
if msg != '':
self.reduction_config[block][name][son_name][i]['msg'] = msg
elif son_name != "":
for i in range(len(self.reduction_config[block][name])):
if self.reduction_config[block][name][i]['name'] == son_name:
self.reduction_config[block][name][i][status_name] = status
if msg != '':
self.reduction_config[block][name][i]['msg'] = msg
elif name != "":
self.reduction_config[block][name][status_name] = status
if msg != '':
self.reduction_config[block][name]['msg'] = msg
else:
self.reduction_config[block][status_name] = status
if msg != '':
self.reduction_config[block]['msg'] = msg
public.writeFile(self.reduction_log_path, json.dumps(self.reduction_config))
def reduction_env(self, conf):
env_conf = conf["env_list"]
connect = {
"php": self.reduction_php,
"mysql": self.reduction_mysql,
"mongo": self.reduction_mongo,
"redis": self.reduction_redis,
"pgsql": self.reduction_pgsql,
"nginx": self.reduction_nginx,
"apache": self.reduction_apache,
"ftp": self.reduction_ftp,
}
for k, v in env_conf.items():
for i in v:
if i['status'] == 1:
# 取备份的信息
try:
connect[k](i)
except:
print(public.get_error_info())
self.change_reduction_status("env_list", k, i["name"], "reduction_status", 2, msg="{}{}还原失败,程序出错".format(k, i["name"]))
else:
if i['status'] != 3 and i['name']:
self.change_reduction_status("env_list", k, i["name"], "reduction_status", 4, msg="{}{}未备份".format(k, i["name"]))
# 检测进程是否存在
def cehck_pid_status(self, pid):
try:
ps = psutil.Process(int(pid))
return True
except:
return False
def reduction_php(self, conf):
self.print_log("->>>开始还原PHP环境:{}".format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", -1, msg='开始还原php{}环境'.format(conf["name"]))
pid_file = '/www/server/php/{}/var/run/php-fpm.pid'.format(conf["name"])
if conf["name"] == '52':
pid_file = '/www/server/php/{}/logs/php-fpm.pid'.format(conf["name"])
php_backup_config = [j for i, j in self.backup_task_data['env_list']["php"].items() if i == conf["name"]]
if not php_backup_config or isinstance(php_backup_config, str):
self.print_log("获取php{}备份信息失败".format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", 2, msg="获取php{}备份信息失败".format(conf["name"]))
return
php_backup_config = php_backup_config[0]
# 开始检测当前php是否安装
php_path = '/www/server/php/{}'.format(conf["name"])
if not os.path.exists(php_path):
self.print_log("php{}未安装,开始安装".format(conf["name"]))
# 安装指定的php版本
public.ExecShell('cd /www/server/panel/install && /bin/bash install_soft.sh 1 install php {}.{} &>> {}'.format(conf["name"][0], conf["name"][1], self.logs_file))
self.print_log('php{}安装完成,检测安装状态中.....'.format(conf["name"]))
pid = public.readFile(pid_file)
if not self.cehck_pid_status(pid):
public.ExecShell('/etc/init.d/php-fpm-{} start'.format(conf["name"]))
time.sleep(0.1)
pid = public.readFile(pid_file)
if not self.cehck_pid_status(pid):
self.print_log('php{}安装失败'.format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", 2, msg="php{}安装失败".format(conf["name"]))
public.ExecShell("rm -rf /www/server/php/{}".format(conf["name"]))
return
self.print_log('php{}安装完成'.format(conf["name"]))
else:
self.print_log('php{}已安装'.format(conf["name"]))
self.print_log('状态检测中.....')
pid = public.readFile(pid_file)
if not self.cehck_pid_status(pid):
self.print_log('php{}未启动,正在启动中......'.format(conf["name"]))
public.ExecShell('/etc/init.d/php-fpm-{} start'.format(conf["name"]))
time.sleep(0.1)
pid = public.readFile(pid_file)
self.print_log(pid)
if not self.cehck_pid_status(pid):
self.print_log('php{}启动失败'.format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", 2, msg="php{}启动失败".format(conf["name"]))
return
self.print_log('php{}已启动'.format(conf["name"]))
# 开始安装拓展
so_path = "/www/server/php/{}/lib/php/extensions/".format(conf["name"])
if os.path.exists(so_path) and len(os.listdir(so_path)) > 0:
so_path = os.path.join(so_path, os.listdir(so_path)[0])
development_list = php_backup_config["php_development"]
else:
development_list = []
now_development_list = ajax.ajax().GetPHPConfig(public.to_dict_obj({"version": conf["name"]}))['libs']
now_development_list = {i['name']: i for i in now_development_list}
install_err = []
for development in development_list:
development = development.lower()
if development.lower() in now_development_list.keys() and now_development_list[development.lower()]["status"] != True and conf["name"] in now_development_list[development.lower()][
"versions"] and not os.path.exists(os.path.join(so_path, "{}.so".format(development))):
self.print_log("开始安装php{}拓展:{}".format(conf["name"], development))
public.ExecShell(
'wget -O {sh_name}.sh https://download.bt.cn/install/1/{sh_name}.sh && sh {sh_name}.sh install {php_version} $>> {log}'.format(sh_name=development.lower(),
php_version=conf["name"],
log=self.logs_file))
# 检测是否安装成功
if 'swoole' in development:
development = 'swoole'
res = public.ExecShell('/www/server/php/{}/bin/php -m'.format(conf["name"]))
if development in res[0].lower() and "{}.so".format(development) in os.listdir(so_path):
self.print_log("php{}拓展:{}安装成功".format(conf["name"], development))
else:
self.print_log("php{}拓展:{}安装失败".format(conf["name"], development))
install_err.append(development)
# 安装失败的重新安装
for development in install_err:
development = development.lower()
self.print_log("开始安装php{}拓展:{}".format(conf["name"], development))
public.ExecShell(
'wget -O {sh_name}.sh https://download.bt.cn/install/1/{sh_name}.sh && sh {sh_name}.sh install {php_version} $>> {log}'.format(sh_name=development.lower(), php_version=conf["name"],
log=self.logs_file))
# 检测是否安装成功
if 'swoole' in development:
development = 'swoole'
res = public.ExecShell('/www/server/php/{}/bin/php -m'.format(conf["name"]))
try:
if development in res[0].lower() or "{}.so".format(development) in os.listdir(so_path):
self.print_log("php{}拓展:{}安装成功".format(conf["name"], development))
else:
self.print_log("php{}拓展:{}安装失败".format(conf["name"], development))
except:
pass
# 开始还原php配置文件
php_ini_path = '/www/server/php/{}/etc/php.ini'.format(conf["name"])
php_fpm_path = '/www/server/php/{}/etc/php-fpm.conf'.format(conf["name"])
php_ini_res = self.uncopy_file(php_backup_config["php_ini"], php_ini_path, isfile=True)
php_fpm_res = self.uncopy_file(php_backup_config["php_fpm"], php_fpm_path, isfile=True)
if not php_ini_res or not php_fpm_res:
self.print_log("还原php{}配置文件失败".format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", 2, msg="还原php{}配置文件失败".format(conf["name"]))
return
# 安全防护拓展 移除
if not os.path.exists(so_path) or 'security_notice.so' not in os.listdir(so_path):
public.ExecShell("sed -i '/security_notice/ s/.*/ /' {}".format(php_ini_path))
public.ExecShell("sed -i '/security.notice/ s/.*/ /' {}".format(php_fpm_path))
self.print_log("还原php{}配置文件完成".format(conf["name"]))
# 重载php
public.ExecShell('/etc/init.d/php-fpm-{} reload'.format(conf["name"]))
self.print_log("->>>还原PHP环境:{}完成".format(conf["name"]))
self.change_reduction_status("env_list", "php", conf["name"], "reduction_status", 1, msg="还原php{}配置文件完成".format(conf["name"]))
def reduction_mysql(self, conf):
self.print_log("->>>开始还原mysql环境:{}".format(conf["name"]))
self.print_log(conf)
backup_mysql_conf = self.backup_task_data['env_list']['mysql']
self.change_reduction_status("env_list", "mysql", conf['name'], status_name="reduction_status", status=-1, msg="开始还原mysql{}环境".format(conf["name"]))
self.print_log(backup_mysql_conf)
# 检测mysql是否安装和版本是否相同
mysql_path = '/www/server/mysql/version.pl'
if not os.path.exists(mysql_path):
self.print_log("mysql未安装,开始安装mysql{}".format(backup_mysql_conf["version"]))
public.ExecShell('mkdir -p /www/server/mysql')
version = backup_mysql_conf["version"]
if version.count('.') > 1:
version = version.split('.')[:2]
version = '.'.join(version)
public.ExecShell("/bin/bash /www/server/panel/install/install_soft.sh 1 install mysql {} &>> {}".format(version, self.logs_file))
mysql_version = public.readFile('/www/server/mysql/version.pl')
if not mysql_version:
self.print_log("mysql读取版本异常,请手动检查")
self.change_reduction_status("env_list", "mysql", conf['name'], status_name="reduction_status", status=2, msg="mysql读取版本异常,请手动检查")
else:
mysql_version = mysql_version.strip()
if mysql_version != conf["name"]:
self.print_log("mysql版本不一致,请手动操作将mysql版本升级到{}版本".format(conf["name"]))
self.change_reduction_status("env_list", "mysql", conf['name'], status_name="reduction_status", status=2, msg="mysql版本不一致,请手动操作将mysql版本升级到{}版本".format(conf["name"]))
return
else:
self.print_log("mysql版本一致")
# 恢复配置文件
self.print_log('开始恢复mysql配置文件')
config_path = '/etc/my.cnf'
res = self.uncopy_file(backup_mysql_conf["mysql_conf"], config_path, isfile=True, md5=backup_mysql_conf["mysql_conf_md5"])
if not res:
self.change_reduction_status("env_list", "mysql", conf['name'], status_name="reduction_status", status=2, msg="还原mysql{}配置文件失败".format(conf["name"]))
self.print_log("->>>还原mysql环境:{}完成".format(conf["name"]))
self.change_reduction_status("env_list", "mysql", conf['name'], status_name="reduction_status", status=1, msg="还原mysql{}完成".format(conf["name"]))
def reduction_mongo(self, conf):
self.print_log("->>>开始还原mongo环境:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原mongo环境:{}完成".format(conf["name"]))
def reduction_redis(self, conf):
self.print_log("->>>开始还原redis环境:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原redis环境:{}完成".format(conf["name"]))
def reduction_pgsql(self, conf):
self.print_log("->>>开始还原pgsql环境:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原pgsql环境:{}完成".format(conf["name"]))
def reduction_nginx(self, conf):
self.print_log("->>>开始还原nginx环境:{}".format(conf["name"]))
# self.print_log(conf)
# self.print_log(self.backup_task_data['env_list']['nginx'])
self.change_reduction_status("env_list", "nginx", conf['name'], status_name="reduction_status", status=-1, msg="开始还原nginx环境")
nginx_path = '/www/server/nginx'
# 关闭nginx 服务
if os.path.exists(nginx_path):
public.ExecShell("/etc/init.d/nginx stop")
self.print_log('关闭nginx服务')
self.print_log("开始恢复nginx")
flag = 1
# 恢复nginx文件
res = self.uncopy_file(self.backup_task_data['env_list']['nginx']["nginx_file"], "/www/server/nginx/", unbak=True)
if not res:
flag = 0
# 还原nginx启动文件
if self.backup_task_data['env_list']['nginx']["nginx_cmd"]:
res = self.uncopy_file(self.backup_task_data['env_list']['nginx']["nginx_cmd"], "/etc/init.d/nginx", isfile=True, md5=self.backup_task_data['env_list']['nginx']["nginx_cmd_md5"])
if not res:
flag = 0
if self.backup_task_data['env_list']['nginx']["nginx_systemd"]:
res = self.uncopy_file(self.backup_task_data['env_list']['nginx']["nginx_systemd"], "/lib/systemd/system/nginx.service", isfile=True,
md5=self.backup_task_data['env_list']['nginx']["nginx_systemd_md5"])
if not res:
flag = 0
if not flag:
# 还原之前的版本
if os.path.exists("/www/server/nginx_bak"):
public.ExecShell("rm -rf /www/server/nginx")
public.ExecShell("mv /www/server/nginx_bak /www/server/nginx")
if os.path.exists("/etc/init.d/nginx_bak"):
public.ExecShell("rm -rf /etc/init.d/nginx")
public.ExecShell("mv /etc/init.d/nginx_bak /etc/init.d/nginx")
if os.path.exists("/lib/systemd/system/nginx.service_bak"):
public.ExecShell("rm -rf /lib/systemd/system/nginx.service")
public.ExecShell("mv /lib/systemd/system/nginx.service_bak /lib/systemd/system/nginx.service")
self.change_reduction_status("env_list", "nginx", conf['name'], status_name="reduction_status", status=2, msg="还原nginx失败")
public.ExecShell('/etc/init.d/nginx start')
self.print_log("还原nginx失败")
return
# 启动nginx
public.ExecShell("/etc/init.d/nginx start")
self.change_reduction_status("env_list", "nginx", conf['name'], status_name="reduction_status", status=1, msg="还原nginx完成")
self.print_log("->>>还原nginx环境:{}完成".format(conf["name"]))
def reduction_apache(self, conf):
self.print_log("->>>开始还原apache环境:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("env_list", "apache", conf['name'], status_name="reduction_status", status=-1, msg="开始还原apache{}环境".format(conf["name"]))
self.print_log(self.backup_task_data['env_list']['apache'])
apache_path = '/www/server/apache/version.pl'
if not os.path.exists(apache_path):
# 安装apache
public.ExecShell('mkdir -p /www/server/apache')
version = self.backup_task_data['env_list']['apache']["version"]
if version.count('.') > 1:
version = version.split('.')[:2]
version = '.'.join(version)
public.ExecShell("/bin/bash /www/server/panel/install/install_soft.sh 1 install apache {} &>> {}".format(version, self.logs_file))
apache_version = public.readFile('/www/server/apache/version.pl')
if not apache_version:
self.print_log("apache读取版本异常,请手动检查")
self.change_reduction_status("env_list", "apache", conf['name'], status_name="reduction_status", status=2, msg="apache读取版本异常,请手动检查")
else:
apache_version = apache_version.strip()
if apache_version != conf["name"]:
public.ExecShell('rm -rf /www/server/apache')
self.print_log('apache版本不一致,升级apache到{}版本'.format(conf["name"]))
version = conf["name"]
if version.count('.') > 1:
version = version.split('.')[:2]
version = '.'.join(version)
public.ExecShell("/bin/bash /www/server/panel/install/install_soft.sh 1 install apache {} &>> {}".format(version, self.logs_file))
# 恢复配置文件
res = self.uncopy_file(self.backup_task_data['env_list']['apache']["apache_conf"], "/www/server/apache/", isfile=True, md5=self.backup_task_data['env_list']['apache']["apache_conf_md5"])
if not res:
self.change_reduction_status("env_list", "apache", conf['name'], status_name="reduction_status", status=2, msg="还原apache{}配置文件失败".format(conf["name"]))
self.print_log("->>>还原apache环境:{}完成".format(conf["name"]))
def reduction_ftp(self, conf):
self.print_log("->>>开始还原ftp环境:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("env_list", "ftp", conf['name'], status_name="reduction_status", status=-1, msg="开始还原ftp{}环境".format(conf["name"]))
self.print_log(self.backup_task_data['env_list']['ftp'])
# 检测ftp是否安装
ftp_path = '/www/server/pure-ftpd'
if not os.path.exists(ftp_path):
# 安装ftp
version = self.backup_task_data['env_list']['ftp']["version"]
public.ExecShell("/bin/bash /www/server/panel/install/install_soft.sh 1 install pure-ftpd {}&>> {}".format(version, self.logs_file))
# 恢复配置文件
self.print_log('开始恢复ftp配置文件')
res = self.uncopy_file(self.backup_task_data['env_list']['ftp']["ftp_conf"], "/www/server/pure-ftpd/etc/pure-ftpd.conf", md5=self.backup_task_data['env_list']['ftp']["ftp_conf_md5"])
if not res:
self.change_reduction_status("env_list", "ftp", conf['name'], status_name="reduction_status", status=2, msg="还原ftp{}配置文件失败".format(conf["name"]))
# 恢复配置文件
public.ExecShell('rm -rf /www/server/pure-ftpd')
public.ExecShell('mv /www/server/pure-ftpd/etc/pure-ftpd.conf_bak /www/server/pure-ftpd/etc/pure-ftpd.conf')
# 检查日志开启状态
self.print_log('开始检查ftp日志开启状态')
rsyslog_path = "/etc/rsyslog.conf"
cofig = public.readFile(rsyslog_path)
if "ftp.*" in cofig:
log_status = 1
else:
log_status = 0
if self.backup_task_data['env_list']['ftp']["log_status"] != log_status:
if self.backup_task_data['env_list']['ftp']["log_status"]:
public.ExecShell("sed -i '/ftp\.\*/d' {}".format(rsyslog_path))
else:
public.ExecShell("echo 'ftp.* /var/log/pureftpd.log' >> {}".format(rsyslog_path))
public.ExecShell('/etc/init.d/rsyslog restart')
self.print_log('ftp日志开启状态恢复完成')
self.change_reduction_status("env_list", "ftp", conf['name'], status_name="reduction_status", status=1, msg="还原ftp{}完成".format(conf["name"]))
self.print_log("->>>还原ftp环境:{}完成".format(conf["name"]))
def reduction_data(self, conf):
data_conf = conf["data_list"]
connect = {
"php": self.reduction_php_data,
"java": self.reduction_java_data,
"node": self.reduction_node_data,
"python": self.reduction_python_data,
"other": self.reduction_other_data,
"net": self.reduction_net_data,
"proxy": self.reduction_proxy_data,
"html": self.reduction_html_data,
"mysql": self.reduction_mysql_data,
"mongodb": self.reduction_mongodb_data,
"redis": self.reduction_redis_data,
"pgsql": self.reduction_pgsql_data,
"ftp_list": self.reduction_ftp_data,
"terminal_list": self.reduction_terminal_data,
"crontab_list": self.reduction_crontab_data,
"port_list": self.reduction_port_data,
"ip_list": self.reduction_ip_data,
"port_redirect": self.reduction_port_redirect_data,
"area_list": self.reduction_area_data,
"ssh_config": self.reduction_ssh_config_data,
"words": self.reduction_words_data,
"sqlite": self.reduction_sqlite_data,
}
for k, v in data_conf.items():
if k in ['site_list', 'sql_list', 'config_list', "safety"]:
for type, dates in v.items():
if type not in connect.keys():
pass
for data in dates:
if int(data['status']) in [1, -1, 2] and data['backup_status'] == 1:
try:
connect[type.lower()](data)
except:
print(public.get_error_info())
self.change_reduction_status("data_list", k, type, "reduction_status", 2, data["name"], msg="{}还原失败,程序出错".format(data["name"]))
else:
self.change_reduction_status("data_list", k, type, "reduction_status", 4, data["name"], msg="{}备份状态异常".format(data["name"]))
elif k in ['ftp_list']:
for i in v:
if i['status'] in [1, -1, 2] and i['backup_status'] == 1:
try:
connect[k](i)
except:
print(public.get_error_info())
self.change_reduction_status("data_list", "ftp_list", i["name"], "reduction_status", 2, msg="{}还原失败,程序出错".format(i["name"]))
else:
self.change_reduction_status("data_list", "ftp_list", i["name"], "reduction_status", 4, msg="{}备份状态异常".format(i["name"]))
else:
for j in v:
if j['status'] in [1, -1, 2] and j['backup_status'] == 1:
try:
connect[k](j)
except:
print(public.get_error_info())
self.change_reduction_status("data_list", k, j["name"], "reduction_status", 2, msg="{}还原失败,程序出错".format(j["name"]))
else:
if j['status'] != 3 and j['name']:
self.change_reduction_status("data_list", k, j["name"], "reduction_status", 4, msg="{}备份状态异常".format(j["name"]))
def reduction_php_data(self, conf):
self.print_log(")->>>开始还原PHP数据:{}".format(conf["name"]))
backup_conf_config = self.backup_task_data.get('data_list', {}).get('site_list', {}).get('php', {}).get(conf['name'], {})
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", -1, conf["name"], msg="开始还原php{}数据".format(conf["name"]))
if not backup_conf_config:
self.print_log("获取php{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="获取php{}备份信息失败".format(conf["name"]))
return
# 查看当前网站配置
now_site_config = public.M('sites').where('name=?', (conf['name'],)).select()
site_info = backup_conf_config["site_info"]
# php动态项目特殊处理:
import panelSite
from mod.project.php.php_asyncMod import main as php_asyncMod
is_phpmod = False
if 'project_config' in site_info.keys():
site_info['project_config'] = json.loads(site_info['project_config'])
if 'type' in site_info['project_config'].keys() and site_info['project_config']['type'].lower() == 'phpmod':
is_phpmod = True
if now_site_config and isinstance(now_site_config, list):
# 同步网站的sql配置
now_site_config = now_site_config[0]
site_info['ps'] = now_site_config['ps']
site_info['id'] = now_site_config['id']
site_info.pop('index')
if isinstance(site_info.get('project_config', ''), dict):
site_info['project_config'] = json.dumps(site_info['project_config'])
site_info.pop('php_version')
public.M('sites').where('name=? and id=?', (site_info["name"], now_site_config["id"])).update(site_info)
else:
if not is_phpmod:
args = public.dict_obj()
args.path = site_info['path']
args.ftp = False
args.type = 'PHP'
args.type_id = site_info['type_id']
args.ps = site_info['ps']
args.port = '80'
args.version = site_info['php_version']
args.need_index = 0
args.need_404 = 0
args.sql = False
args.codeing = 'utf8mb4'
args.webname = json.dumps({"domain": site_info['name'], "domainlist": [], "count": 0})
panelSite.panelSite().AddSite(args)
else:
args = public.dict_obj()
args.site_path = site_info['path']
args.project_cmd = site_info['project_config']['start_cmd']
args.install_dependence = '0'
args.php_version = site_info['project_config']['php_version']
args.sql = False
args.project_ps = site_info['ps']
args.project_port = ''
args.open_proxy = '0'
args.project_proxy_path = ''
args.run_user = site_info['project_config']['run_user']
args.composer_version = ''
args.webname = json.dumps({"domain": site_info['name'], "domainlist": [], "count": 0})
php_asyncMod().create_project(args)
self.print_log('面板网站存储信息同步完成')
# 恢复网站文件
site_path = site_info['path']
res = self.uncopy_file(backup_conf_config["site_file"], site_path, isfile=False, unbak=True)
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}网站文件失败".format(conf["name"]))
self.print_log("还原php{}网站文件失败".format(conf["name"]))
return
# 同步网站域名
site_id = public.M('sites').where('name=?', (conf['name'],)).getField('id')
domains = backup_conf_config["domains"]
if domains and isinstance(domains, list):
domains = {i["name"]: i for i in domains}
sql = public.M('domain')
now_domains = sql.where('pid=?', (site_id,)).select()
if now_domains and isinstance(now_domains, list):
now_domains = {i["name"]: i for i in now_domains}
else:
now_domains = {}
print(domains)
print(now_domains)
# 删除多余的域名
del_domains = [i for i in now_domains.keys() if i not in domains.keys()]
if del_domains:
public.M('domain').where('name in (?)', (','.join(del_domains))).delete()
self.print_log('删除多余的域名成功:{}'.format(','.join(del_domains)))
# 修改域名信息
for domain, value in domains.items():
if domain not in now_domains.keys():
self.print_log('添加域名:{}'.format(domain))
value.pop('id')
value['pid'] = site_id
public.M('domain').insert(value)
else:
domains[domain].pop('id')
value['pid'] = site_id
public.M('domain').where('name=?', (domain,)).update(value)
self.print_log("php{}网站域名恢复完成".format(conf["name"]))
# 恢复网站nginx和apache配置文件
self.print_log('开始恢复php{}网站nginx和apache配置文件'.format(conf["name"]))
nginx_conf_path = '/www/server/panel/vhost/nginx/{}.conf'.format(conf["name"])
apache_conf_path = '/www/server/panel/vhost/apache/{}.conf'.format(conf["name"])
if backup_conf_config['nginx_config'] and os.path.exists(backup_conf_config['nginx_config']):
res = self.uncopy_file(backup_conf_config["nginx_config"], nginx_conf_path, isfile=True, md5=backup_conf_config["nginx_config_md5"])
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}nginx配置文件失败".format(conf["name"]))
self.print_log("还原php{}nginx配置文件失败".format(conf["name"]))
return
if backup_conf_config['apache_config'] and os.path.exists(backup_conf_config['apache_config']):
res = self.uncopy_file(backup_conf_config["apache_config"], apache_conf_path, isfile=True, md5=backup_conf_config["apache_config_md5"])
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}apache配置文件失败".format(conf["name"]))
self.print_log("还原php{}apache配置文件失败".format(conf["name"]))
return
self.print_log("php{}网站nginx和apache配置文件恢复完成".format(conf["name"]))
# 恢复网站重定向
self.print_log('开始恢复php{}网站重定向'.format(conf["name"]))
redirect_list = backup_conf_config["redirect_list"]
__redirectfile = "/www/server/panel/data/redirect.conf"
redirectconf = json.loads(public.readFile(__redirectfile))
red_name = {j['sitename']: i for i, j in enumerate(redirectconf)}
for i in redirect_list:
if i["sitename"] not in red_name.keys():
redirectconf.append(i)
elif i['sitename'] in red_name.keys():
redirectconf[red_name[i['sitename']]] = i
public.writeFile(__redirectfile, json.dumps(redirectconf))
res_nginx = self.uncopy_file(backup_conf_config["redirect_nginx_data"], '/www/server/panel/vhost/nginx/redirect/{}'.format(conf["name"]), isfile=False)
res_apache = self.uncopy_file(backup_conf_config["redirect_apache_data"], '/www/server/panel/vhost/apache/redirect/{}'.format(conf["name"]), isfile=False)
if res_nginx and res_apache:
self.print_log("恢复重定向成功")
else:
self.print_log("恢复重定向失败")
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="恢复重定向失败")
# 删除多余得重定向
old_list = [i['redirectname'] for i in redirect_list]
now_redirect_list = [i['redirectname'] for i in redirectconf if i['sitename'] == conf["name"]]
for redirect in now_redirect_list:
if redirect not in old_list:
if is_phpmod:
res = php_asyncMod().remove_project_redirect(public.to_dict_obj({"sitename": conf["name"], "redirectname": redirect}))
else:
res = panelSite.panelSite().DeleteRedirect(public.to_dict_obj({"siteName": conf["name"], "redirectName": redirect}))
if res['status']:
self.print_log("删除多余重定向:{}成功".format(redirect))
else:
self.print_log("删除多余重定向:{}失败".format(redirect))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="删除多余重定向:{}失败".format(redirect))
self.print_log("php{}网站重定向恢复完成".format(conf["name"]))
# 恢复反向代理
self.print_log('开始恢复php{}网站反向代理'.format(conf["name"]))
proxy_list = backup_conf_config["proxy_list"]
if not is_phpmod:
__proxyfile = '{}/data/proxyfile.json'.format(public.get_panel_path())
else:
__proxyfile = "{}/data/mod_proxy_file.conf".format(public.get_panel_path())
try:
proxyUrl = json.loads(public.readFile(__proxyfile))
except:
proxyUrl = {}
proxy_name = {j['sitename']: i for i, j in enumerate(proxyUrl)}
for i in proxy_list:
if i["sitename"] not in proxy_name.keys():
proxyUrl.append(i)
elif i['sitename'] in proxy_name.keys():
proxyUrl[proxy_name[i['sitename']]] = i
public.writeFile(__proxyfile, json.dumps(proxyUrl))
res_n = self.uncopy_file(backup_conf_config["proxy_nginx_data"], '/www/server/panel/vhost/nginx/proxy/{}'.format(conf["name"]), isfile=False)
res_a = self.uncopy_file(backup_conf_config["proxy_apache_data"], '/www/server/panel/vhost/apache/proxy/{}'.format(conf["name"]), isfile=False)
if not res_n or not res_a:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="恢复nginx反向代理失败")
self.print_log("恢复反向代理失败")
# 删除多余的反向代理
old_list = [i['proxyname'] for i in proxy_list]
proxy_name = [i for i in proxyUrl if i['sitename'] == conf["name"]]
for proxy in proxy_name:
if proxy['proxyname'] not in old_list:
if is_phpmod:
res = php_asyncMod().remove_proxy(public.to_dict_obj({"sitename": conf["name"], "proxyname": proxy['proxyname']}))
else:
res = panelSite.panelSite().DeleteProxy(public.to_dict_obj({"siteName": conf["name"], "proxyName": proxy['proxyname']}))
if not res['status']:
self.print_log("删除多余反向代理:{}失败".format(proxy['proxyname']))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="删除多余反向代理:{}失败".format(proxy['proxyname']))
return
self.print_log("删除多余反向代理:{}成功".format(proxy['proxyname']))
self.print_log("php{}网站反向代理恢复完成".format(conf["name"]))
# 开始恢复ssl证书
self.print_log('开始恢复php{}网站ssl证书'.format(conf["name"]))
if backup_conf_config["ssl_info"]:
ssl_path = '/www/server/panel/vhost/cert/{}'.format(conf["name"])
res = self.uncopy_file(backup_conf_config["ssl_info"], ssl_path, isfile=False)
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}ssl证书失败".format(conf["name"]))
self.print_log("还原php{}ssl证书失败".format(conf["name"]))
return
self.print_log("php{}网站ssl证书恢复完成".format(conf["name"]))
else:
self.print_log("php{}网站无ssl证书".format(conf["name"]))
# 开始恢复目录加密访问
self.print_log('开始恢复php{}网站目录加密访问'.format(conf["name"]))
if backup_conf_config["dir_auth_json"]:
dir_auth_path = '/tmp/{}'.format(conf["name"])
self.uncopy_file(backup_conf_config["dir_auth_json"], dir_auth_path, unbak=True)
if os.path.exists(dir_auth_path):
res = public.readFile(dir_auth_path)
if res:
res = json.loads(res)
data = res.get(conf["name"], [])
if data:
config_path = '/www/server/panel/data/site_dir_auth.json'
if os.path.exists(config_path):
res = public.readFile(config_path)
if res:
res = json.loads(res)
else:
res = {}
res[conf["name"]] = data
public.writeFile(config_path, json.dumps(res))
else:
conf = {conf["name"]: data}
public.writeFile(config_path, json.dumps(conf))
if backup_conf_config["dir_auth_file"]:
res = self.uncopy_file(backup_conf_config["dir_auth_file"], '/www/server/panel/vhost/nginx/dir_auth/{}'.format(conf["name"]), isfile=False)
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}目录加密访问失败".format(conf["name"]))
self.print_log("还原php{}目录加密访问失败".format(conf["name"]))
return
self.print_log("php{}网站目录加密访问恢复完成".format(conf["name"]))
# 开始恢复子目录域名绑定
if backup_conf_config["banding"]:
if is_phpmod:
pass
else:
self.print_log('开始恢复php{}网站子目录域名绑定'.format(conf["name"]))
res = panelSite.panelSite().GetDirBinding(public.to_dict_obj({"id": site_id}))
banding_list = res["binding"]
banding_list = {i["domain"]: i for i in banding_list}
old_list = {i["domain"]: i for i in backup_conf_config["banding"]}
for k, v in old_list.items():
if k not in banding_list.keys():
res = panelSite.panelSite().AddDirBinding(public.to_dict_obj({"id": site_id, "domain": v["domain"], "dirName": v["path"]}))
if not res["status"]:
self.print_log("恢复子目录域名绑定:{}失败".format(k))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="恢复子目录域名绑定:{}失败".format(k))
return
self.print_log("恢复子目录域名绑定:{}成功".format(k))
for k, v in banding_list.items():
if k not in old_list.keys():
res = panelSite.panelSite().DelDirBinding(public.to_dict_obj({"id": v['id']}))
if not res["status"]:
self.print_log("删除多余子目录域名绑定:{}失败".format(k))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="删除多余子目录域名绑定:{}失败".format(k))
return
self.print_log("删除多余子目录域名绑定:{}成功".format(k))
# 开始恢复伪静态
if backup_conf_config["pseudo_static_data"]:
self.print_log('开始恢复php{}网站伪静态'.format(conf["name"]))
res = self.uncopy_file(backup_conf_config["pseudo_static_data"], '/www/server/panel/vhost/rewrite/{}.conf'.format(conf["name"]), isfile=True,
md5=backup_conf_config["pseudo_static_data_md5"])
if not res:
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 2, conf["name"], msg="还原php{}伪静态失败".format(conf["name"]))
self.print_log("还原php{}伪静态失败".format(conf["name"]))
return
self.print_log("php{}网站伪静态恢复完成".format(conf["name"]))
# 还原网站相关数据库
self.print_log('开始还原php{}网站相关数据库'.format(conf["name"]))
if backup_conf_config["database_info"]:
self.reduction_mysql_data(backup_conf_config['database_info'])
if backup_conf_config["ftp_info"]:
self.reduction_ftp_data(backup_conf_config["ftp_info"])
self.print_log('php{}网站相关数据库恢复完成'.format(conf["name"]))
self.print_log("->>>还原PHP数据:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "php", "reduction_status", 1, conf["name"], msg="还原成功!")
def reduction_java_data(self, conf):
self.print_log("->>>开始还原java数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原java数据:{}完成".format(conf["name"]))
def reduction_node_data(self, conf):
self.print_log("->>>开始还原node数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原node数据:{}完成".format(conf["name"]))
def reduction_python_data(self, conf):
self.print_log("->>>开始还原python数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原python数据:{}完成".format(conf["name"]))
def reduction_other_data(self, conf):
self.print_log("->>>开始还原other数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原other数据:{}完成".format(conf["name"]))
def reduction_net_data(self, conf):
self.print_log("->>>开始还原net数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原net数据:{}完成".format(conf["name"]))
def reduction_proxy_data(self, conf):
self.print_log("->>>开始还原proxy数据:{}".format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", -1, conf["name"], msg="开始还原proxy{}数据".format(conf["name"]))
proxy_config_path = "/www/server/proxy_project/sites/{}".format(conf["name"])
nginx_config_path = "/www/server/panel/vhost/nginx/{}.conf".format(conf["name"])
apache_config_path = "/www/server/panel/vhost/apache/{}.conf".format(conf["name"])
# 恢复面板存储得网站信息
self.print_log('开始恢复数据库存储信息proxy:{}'.format(conf["name"]))
site_info = self.backup_task_data['data_list']['site_list']['proxy'].get(conf["name"], {})
if not site_info:
self.print_log("获取proxy{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 2, conf["name"], msg="获取proxy{}备份信息失败")
return
site_db_info = site_info["site_info"][0]
now_site_info = public.M('sites').where('name=?', (conf["name"],)).select()
if now_site_info and not isinstance(now_site_info, str):
now_site_info = now_site_info[0]
site_db_info.pop('id')
now_site_info.update(site_db_info)
now_site_info.pop('index')
public.M('sites').where('name=? and project_type="proxy"', (conf["name"],)).update(now_site_info)
else:
proxy_cache_dir = "/www/wwwroot/{}/proxy_cache_dir".format(conf["name"])
if not os.path.exists(proxy_cache_dir):
public.ExecShell('mkdir -p {}'.format(proxy_cache_dir))
if os.path.exists(proxy_cache_dir):
public.ExecShell('chown -R www:www {}'.format(proxy_cache_dir))
public.ExecShell('chmod -R 755 {}'.format(proxy_cache_dir))
self.print_log('创建proxy{}缓存目录成功'.format(conf["name"]))
else:
self.print_log('创建proxy{}缓存目录失败'.format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 2, conf["name"], msg="创建proxy{}缓存目录失败{}".format(conf["name"], proxy_cache_dir))
return
site_db_info.pop('id')
site_db_info['project_type'] = 'proxy'
site_db_info.pop('index')
public.M('sites').insert(site_db_info)
self.print_log('面板网站存储信息同步完成')
# 还原网站配置文件
self.print_log('开始恢复proxy{}网站配置文件'.format(conf["name"]))
res = self.uncopy_file(site_info["proxy_config"], proxy_config_path, isfile=True, md5=site_info["proxy_config_md5"])
if not res:
self.print_log('还原proxy{}配置文件失败'.format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 2, conf["name"], msg="还原proxy{}配置文件失败".format(conf["name"]))
return
self.print_log('proxy{}网站配置文件恢复完成'.format(conf["name"]))
# 恢复nginx和apache配置文件
self.print_log('开始恢复proxy{}网站nginx和apache配置文件'.format(conf["name"]))
if site_info['nginx_config'] and os.path.exists(site_info['nginx_config']):
res = self.uncopy_file(site_info["nginx_config"], nginx_config_path, isfile=True, md5=site_info["nginx_config_md5"])
if not res:
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 2, conf["name"], msg="还原proxy{}nginx配置文件失败".format(conf["name"]))
self.print_log("还原proxy{}nginx配置文件失败".format(conf["name"]))
return
if site_info['apache_config'] and os.path.exists(site_info['apache_config']):
res = self.uncopy_file(site_info["apache_config"], apache_config_path, isfile=True, md5=site_info["apache_config_md5"])
if not res:
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 2, conf["name"], msg="还原proxy{}apache配置文件失败".format(conf["name"]))
self.print_log("还原proxy{}apache配置文件失败".format(conf["name"]))
return
self.print_log('proxy{}网站nginx和apache配置文件恢复完成'.format(conf["name"]))
self.print_log("->>>还原proxy数据:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "site_list", "proxy", "reduction_status", 1, conf["name"], msg="还原成功!")
def reduction_html_data(self, conf):
self.print_log("->>>开始还原html数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原html数据:{}完成".format(conf["name"]))
def reduction_mysql_data(self, conf):
self.print_log("->>>开始还原mysql数据:{}".format(conf["name"]))
bakup_config = self.backup_task_data['data_list']['sql_list']['mysql'].get(conf['name'], {})
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", -1, conf["name"], msg="开始还原mysql{}数据".format(conf["name"]))
if not bakup_config:
self.print_log("获取mysql{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 2, conf["name"], msg="获取mysql{}备份信息失败".format(conf["name"]))
return
if int(bakup_config['mysql_info']['db_type']) == 2:
# 开始恢复远程数据库配置
self.print_log('开始还原mysql{}远程数据库配置'.format(conf["name"]))
cloud_server = bakup_config['cloud_server']
now_cloud_server = public.M('database_servers').where('id=?', (bakup_config['mysql_info']['sid'])).select()
if not now_cloud_server:
public.M('database_servers').insert(cloud_server)
else:
cloud_server.pop('id')
public.M('database_servers').where('id=?', (bakup_config['mysql_info']['sid'])).update(cloud_server)
now_db_info = public.M('databases').where('name=?', (conf["name"],)).select()
if now_db_info:
public.M('databases').where('name=?', (conf["name"],)).delete()
db_info = bakup_config['mysql_info']
db_info.pop('id')
public.M('databases').insert(db_info)
self.print_log('mysql{}远程数据库配置恢复完成'.format(conf["name"]))
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 1, conf["name"], msg="还原mysql{}数据库成功".format(conf["name"]))
return
# 恢复数据库配置
self.print_log('开始恢复mysql{}数据库配置'.format(conf["name"]))
now_db_info = public.M('databases').where('name=?', (conf["name"],)).select()
if now_db_info and not isinstance(now_db_info, str):
now_db_info = now_db_info[0]
now_db_info['conn_config'] = bakup_config['mysql_info']['conn_config']
now_db_info['sid'] = bakup_config['mysql_info']['sid']
public.M('databases').where('name=?', (conf["name"],)).update(now_db_info)
else:
# 创建数据库
args = public.dict_obj()
args.name = bakup_config['mysql_info']['name']
args.db_user = bakup_config['mysql_info']['username']
args.password = bakup_config['mysql_info']['password']
args.dataAccess = "127.0.0.1"
args.address = "127.0.0.1"
args.codeing = 'utf8mb4'
args.dtype = 'MYSQL'
args.ps = bakup_config['mysql_info']['ps']
args.sid = bakup_config['mysql_info']['sid']
args.list_ip = '0.0.0.0/0'
args.host = ''
res = database.database().AddDatabase(args)
if not res['status']:
self.print_log("创建mysql{}数据库失败".format(conf["name"]))
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 2, conf["name"], msg="创建mysql{}数据库失败,{}".format(conf["name"], res['msg']))
return
self.print_log('mysql{}数据库配置恢复完成'.format(conf["name"]))
# 开始恢复数据库权限
self.print_log('开始还原mysql{}数据库权限'.format(conf["name"]))
access = bakup_config["sql_power"]
args = public.dict_obj()
args.name = conf["name"]
args.dataAccess = access
args.address = ''
args.access = access
res = database.database().SetDatabaseAccess(args)
# 恢复数据记录
if not res['status']:
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 2, conf["name"], msg="还原mysql{}数据库权限失败".format(conf["name"]))
self.print_log("还原mysql{}数据库权限失败".format(conf["name"]))
return
self.print_log('mysql{}数据库权限恢复完成'.format(conf["name"]))
# 开始恢复数据库数据
self.print_log('开始还原mysql{}数据库数据'.format(conf["name"]))
res = self.uncopy_file(bakup_config["sql_data"], '/tmp/{}.sql'.format(conf["name"]), isfile=True, unbak=True)
if not res:
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 2, conf["name"], msg="还原mysql{}数据库数据解压失败".format(conf["name"]))
self.print_log("还原mysql{}数据库数据解压失败".format(conf["name"]))
_MYSQL_BIN = public.get_mysql_bin()
db_charset = public.get_database_character(conf['name'])
db_password = public.M("config").where("id=?", (1,)).getField("mysql_root")
if not db_password:
return public.returnMsg(False, "数据库密码为空!请先设置数据库密码!")
try:
db_port = int(panelMysql.panelMysql().query("show global variables like 'port'")[0][1])
except:
db_port = 3306
db_host = "localhost"
db_user = "root"
shell = "'{mysql_bin}' --force --default-character-set='{db_charset}' --host='{db_host}' --port={db_port} --user='{db_user}' --password='{password}' '{db_name}'".format(
mysql_bin=_MYSQL_BIN,
db_charset=db_charset,
db_host=db_host,
db_port=db_port,
db_user=db_user,
password=db_password,
db_name=conf["name"],
)
public.ExecShell("{} < {} &>> {}".format(shell, '/tmp/{}.sql'.format(conf["name"]), self.logs_file))
# 数据检查
now_sql_info = database.database().GetInfo(public.to_dict_obj({'db_name': conf["name"]}))
if now_sql_info['data_size'] != bakup_config["sql_info"]['data_size']:
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 2, conf["name"], msg="还原mysql{}数据库数据失败".format(conf["name"]))
self.print_log("还原mysql{}数据库数据失败".format(conf["name"]))
return
self.print_log('mysql{}数据库数据大小校验完成{}'.format(conf["name"], now_sql_info['data_size']))
self.print_log('mysql{}数据库数据恢复完成'.format(conf["name"]))
self.print_log("->>>还原mysql数据库:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "sql_list", "mysql", "reduction_status", 1, conf["name"], msg="还原mysql{}数据库成功".format(conf["name"]))
def reduction_mongodb_data(self, conf):
self.print_log("->>>开始还原mongodb数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原mongodb数据:{}完成".format(conf["name"]))
def reduction_redis_data(self, conf):
self.print_log("->>>开始还原redis数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原redis数据:{}完成".format(conf["name"]))
def reduction_pgsql_data(self, conf):
self.print_log("->>>开始还原pgsql数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原pgsql数据:{}完成".format(conf["name"]))
def reduction_ftp_data(self, conf):
self.print_log("->>>开始还原ftp数据:{}".format(conf["name"]))
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", -1, msg="开始还原ftp{}数据".format(conf["name"]))
ftp_info = self.backup_task_data['data_list']['ftp_list'].get(conf['name'], {})
if not ftp_info:
self.print_log("获取ftp{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", 2, msg="获取ftp{}备份信息失败".format(conf["name"]))
return
self.print_log('开始恢复ftp{}用户'.format(conf["name"]))
import ftp
now_ftp_info = public.M('ftps').where('name=?', (conf["name"],)).select()
if now_ftp_info and not isinstance(now_ftp_info, str):
now_ftp_info = now_ftp_info[0]
if 'id' in ftp_info.keys(): ftp_info['ftp_info'].pop('id')
now_ftp_info.update(ftp_info['ftp_info'])
public.M('ftps').where('name=?', (conf["name"],)).update(now_ftp_info)
args = public.dict_obj()
args.id = now_ftp_info['id']
args.ftp_username = ftp_info['ftp_info']["name"]
args.new_password = ftp_info['ftp_info']["password"]
args.path = ftp_info['ftp_info']["path"]
res = ftp.ftp().SetUser(args)
if not res['status']:
self.print_log("修改ftp{}用户失败".format(conf["name"]))
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", 2, msg="修改ftp{}用户失败".format(conf["name"]))
return
self.print_log('修改ftp{}用户成功'.format(conf["name"]))
else:
args = public.dict_obj()
args.ftp_username = ftp_info['ftp_info']["name"]
args.ftp_password = ftp_info['ftp_info']["password"]
args.path = ftp_info['ftp_info']["path"]
args.ps = ftp_info['ftp_info']["ps"]
res = ftp.ftp().AddUser(args)
if not res['status']:
self.print_log("创建ftp{}用户失败".format(conf["name"]))
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", 2, msg="创建ftp{}用户失败".format(conf["name"]))
return
self.print_log('创建ftp{}用户成功'.format(conf["name"]))
self.print_log('ftp{}用户恢复完成'.format(conf["name"]))
# 开始恢复ftp数据
self.print_log('开始还原ftp{}数据'.format(conf["name"]))
res = self.uncopy_file(ftp_info["ftp_data"], ftp_info['ftp_info']["path"], isfile=False, unbak=True)
if not res:
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", 2, msg="还原ftp{}数据失败".format(conf["name"]))
self.print_log("还原ftp{}数据失败".format(conf["name"]))
self.print_log('ftp{}数据恢复完成'.format(conf["name"]))
self.print_log("->>>还原ftp:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "ftp_list", conf["name"], "reduction_status", 1, msg="还原ftp{}成功".format(conf["name"]))
def reduction_terminal_data(self, conf):
self.print_log("->>>开始还原terminal数据")
self.print_log(conf)
backup_conf = self.backup_task_data['data_list']['terminal_list'][conf['name']]
terminal = "/www/server/panel/config/ssh_info"
self.print_log('开始还原terminal数据')
res = self.uncopy_file(backup_conf["terminal_data"], terminal, isfile=False)
if not res:
self.change_reduction_status("data_list", "terminal_list", conf["name"], "reduction_status", 2, msg="还原{}数据失败".format(conf["name"]))
self.print_log("还原terminal数据失败")
public.ExecShell('rm -rf {}'.format(terminal))
public.ExecShell('mv {}_bak {} -f'.format(terminal, terminal))
return
self.print_log("->>>还原terminal数据完成")
self.change_reduction_status("data_list", "terminal_list", conf["name"], "reduction_status", 1, msg="还原{}数据成功".format(conf["name"]))
def reduction_crontab_data(self, conf):
self.print_log("->>>开始还原crontab数据:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("data_list", "crontab_list", conf["name"], "reduction_status", -1, msg="开始还原计划任务{}数据".format(conf["name"]))
backup_conf = self.backup_task_data['data_list']['crontab_list']
cron_list = public.M('crontab').select()
cron_list = [i['name'] for i in cron_list]
# 开始恢复计划任务
successful_imports = 0
failed_tasks = []
import crontab
for task in backup_conf['crontab']:
if task['name'] not in cron_list:
# 使用正确的字段和逻辑来创建新的任务字典
new_task = {
"name": task['name'],
"type": task['type'],
"where1": task['where1'],
"hour": task['where_hour'],
"minute": task['where_minute'],
"status": task['status'],
"save": task['save'],
"backupTo": task['backupTo'],
"sType": task['sType'],
"sBody": task['sBody'],
"sName": task['sName'],
"urladdress": task['urladdress'],
"save_local": task['save_local'],
"notice": task['notice'],
"notice_channel": task['notice_channel'],
"db_type": task['db_type'],
"split_type": task['split_type'],
"split_value": task['split_value'],
"keyword": task['keyword'],
"post_param": task['post_param'],
"flock": task['flock'],
"time_set": task['time_set'],
"backup_mode": task['backup_mode'],
"db_backup_path": task['db_backup_path'],
"time_type": task['time_type'],
"special_time": task['special_time'],
"user_agent": task['user_agent'],
"version": task['version'],
"table_list": task['table_list'],
"result": task['result'],
"log_cut_path": task['log_cut_path'],
"rname": task['rname'],
"type_id": task['type_id'],
"second": task.get('second', ''),
}
result = crontab.crontab().AddCrontab(new_task)
if result.get('status', False):
successful_imports += 1
else:
failed_tasks.append(task['name'])
else:
self.print_log("计划任务{}已存在".format(task['name']))
if failed_tasks:
self.print_log("以下任务导入失败:{}".format(failed_tasks))
self.change_reduction_status("data_list", "crontab_list", conf["name"], "reduction_status", 2, msg="以下任务导入失败:{}".format(failed_tasks))
self.print_log("->>>还原计划任务数据:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "crontab_list", conf["name"], "reduction_status", 1, msg="还原计划任务{}成功".format(conf["name"]))
def reduction_port_data(self, conf):
self.print_log("->>>开始还原端口数据:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("data_list", "safety", "port_list", "reduction_status", -1, conf["name"], msg="开始还原端口{}数据".format(conf["name"]))
# 开始恢复端口规则
backup_conf = self.backup_task_data['data_list']['safety']['port_list']['端口规则']
self.print_log(backup_conf)
self.print_log('开始解压端口数据')
res = self.uncopy_file(backup_conf["port_data"], backup_conf['port_data_path'], md5=backup_conf['port_data_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "port_list", "reduction_status", 2, conf["name"], msg="解压端口数据失败")
self.print_log("解压端口数据失败")
return
res = firewall_main().import_rules(public.to_dict_obj({"rule": "port", "file": backup_conf['port_data_path']}))
if not res['status']:
self.change_reduction_status("data_list", "safety", "port_list", "reduction_status", 2, conf["name"], msg="还原端口数据失败" + res['msg'])
self.print_log("还原端口数据失败" + res['msg'])
return
self.print_log("->>>还原端口数据:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "port_list", "reduction_status", 1, conf["name"], msg="还原端口数据成功")
def reduction_port_redirect_data(self, conf):
self.print_log("->>>开始还原端口转发数据:{}".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "port_redirect", "reduction_status", -1, conf["name"], msg="开始还原端口转发{}数据".format(conf["name"]))
self.print_log(conf)
backup_conf = self.backup_task_data['data_list']['safety']['port_redirect']['端口转发']
if not backup_conf:
self.print_log("获取port_redirect{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "port_redirect", "reduction_status", 2, conf["name"], msg="获取端口转发{}备份信息失败".format(conf["name"]))
return
self.print_log('开始解压port_redirect数据')
res = self.uncopy_file(backup_conf["forward"], backup_conf['forward_path'], md5=backup_conf['forward_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "port_redirect", "reduction_status", 2, conf["name"], msg="解压端口转发数据失败")
self.print_log("解压端口转发数据失败")
return
res = firewall_main().import_rules(public.to_dict_obj({"rule": "port_redirect", "file": backup_conf['forward_path']}))
if not res['status']:
self.change_reduction_status("data_list", "safety", "port_redirect", "reduction_status", 2, conf["name"], msg="还原端口转发数据失败" + res['msg'])
self.print_log("还原port_redirect数据失败" + res['msg'])
return
self.change_reduction_status("data_list", "safety", "port_redirect", "reduction_status", 1, conf["name"], msg="还原端口转发数据成功")
self.print_log("->>>还原端口转发数据:{}完成".format(conf["name"]))
def reduction_area_data(self, conf):
self.print_log("->>>开始还原地区限制数据:{}".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "area_list", "reduction_status", -1, conf["name"], msg="开始还原地区限制{}数据".format(conf["name"]))
backup_conf = self.backup_task_data['data_list']['safety']['area_list'][conf['name']]
area_data_path = "/www/server/panel/data/firewall/country.json"
if not backup_conf:
self.print_log("获取地区限制{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "area_list", "reduction_status", 2, conf["name"], msg="获取area{}备份信息失败".format(conf["name"]))
return
# 开始恢复区域封禁
self.print_log('开始解压地区限制数据')
res = self.uncopy_file(backup_conf["area_data"], area_data_path, md5=backup_conf['area_data_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "area_list", "reduction_status", 2, conf["name"], msg="解压地区限制数据失败")
self.print_log("解压地区限制数据失败")
return
res = safe_firewall_main().import_rules(public.to_dict_obj({"rule_name": "country_rule", "file_name": "country.json"}))
if not res['status']:
self.change_reduction_status("data_list", "safety", "area_list", "reduction_status", 2, conf["name"], msg="还原地区限制数据失败" + res['msg'])
self.print_log("还原area数据失败" + res['msg'])
return
self.change_reduction_status("data_list", "safety", "area_list", "reduction_status", 1, conf["name"], msg="还原地区限制数据成功")
self.print_log("->>>还原地区限制数据:{}完成".format(conf["name"]))
def reduction_ip_data(self, conf):
self.print_log("->>>开始还原ip数据:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("data_list", "safety", "ip_list", "reduction_status", -1, conf["name"], msg="开始还原ip{}数据".format(conf["name"]))
backup_conf = self.backup_task_data['data_list']['safety']['ip_list'][conf['name']]
if not backup_conf:
self.print_log("获取ip{}备份信息失败".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "ip_list", "reduction_status", 2, conf["name"], msg="获取ip{}备份信息失败".format(conf["name"]))
return
# 开始恢复IP封禁
self.print_log('开始解压ip数据')
res = self.uncopy_file(backup_conf["ip_data"], backup_conf['ip_list_path'], md5=backup_conf['ip_data_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "ip_list", "reduction_status", 2, conf["name"], msg="解压ip数据失败")
self.print_log("解压ip数据失败")
return
res = firewall_main().import_rules(public.to_dict_obj({"rule": "ip", "file": backup_conf['ip_list_path']}))
if not res['status']:
self.change_reduction_status("data_list", "safety", "ip_list", "reduction_status", 2, conf["name"], msg="还原ip数据失败" + res['msg'])
self.print_log("还原ip数据失败" + res['msg'])
return
self.change_reduction_status("data_list", "safety", "ip_list", "reduction_status", 1, conf["name"], msg="还原ip数据成功")
self.print_log("->>>还原ip数据:{}完成".format(conf["name"]))
def reduction_words_data(self, conf):
self.print_log("->>>开始还原words数据:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("data_list", "safety", "words", "reduction_status", -1, conf["name"], msg="开始还原违规关键词{}数据".format(conf["name"]))
backup_conf = self.backup_task_data['data_list']['safety']['words'][conf['name']]
self.print_log(backup_conf)
# 开始恢复敏感词
self.print_log('开始解压words数据')
words_path = "/www/server/panel/config/thesaurus.json"
res = self.uncopy_file(backup_conf["wrods_data"], words_path, md5=backup_conf['wrod_data_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "words", "reduction_status", 2, conf["name"], msg="还原违规关键词数据失败")
self.print_log("解压words数据失败")
return
self.change_reduction_status("data_list", "safety", "words", "reduction_status", 1, conf["name"], msg="还原违规关键词数据")
self.print_log("->>>还原违规关键词数据:{}完成".format(conf["name"]))
def reduction_sqlite_data(self, conf):
self.print_log("->>>开始还原sqlite数据:{}".format(conf["name"]))
self.print_log(conf)
self.print_log("->>>还原sqlite数据:{}完成".format(conf["name"]))
def reduction_ssh_config_data(self, conf):
self.print_log("->>>开始还原ssh数据:{}".format(conf["name"]))
self.print_log(conf)
self.change_reduction_status("data_list", "safety", "ssh_config", "reduction_status", -1, conf["name"], msg="开始还原ssh{}数据".format(conf["name"]))
backup_conf = self.backup_task_data['data_list']['safety']['ssh_config'][conf['name']]
self.print_log(backup_conf)
ssh_config_path = '/etc/ssh/sshd_config'
self.print_log('开始解压ssh数据')
res = self.uncopy_file(backup_conf["ssh_data"], ssh_config_path, md5=backup_conf['ssh_data_md5'])
if not res:
self.change_reduction_status("data_list", "safety", "ssh_config", "reduction_status", 2, conf["name"], msg="还原ssh数据失败")
self.print_log("还原ssh_config数据失败")
return
public.ExecShell('systemctl restart sshd')
self.print_log("->>>还原ssfig数据:{}完成".format(conf["name"]))
self.change_reduction_status("data_list", "safety", "ssh_config", "reduction_status", 1, conf["name"], msg="还原ssh数据成功")
def create_queue(self, get):
"""
创建任务队列(用于执行备份或还原任务)
:param get: 包含任务 ID 和任务类型的对象
- id (str): 任务的唯一标识符
- type (str): 任务类型,1 表示备份,2 表示还原
"""
# 获取任务 ID 和类型
id = get.id
type = get.type
print(type)
# 根据任务类型设置任务名称
name = '备份任务' if type == 1 else '还原任务'
print(name)
# 导入任务管理模块
import panelTask
print("btpython /www/server/panel/class/panelModel/whole_machine_backupModel.py {} {} &> {}".format(id, type, self.logs_file ))
# 使用任务管理模块创建任务,执行命令通过 `btpython` 调用对应脚本
panelTask.bt_task().create_task(
name, # 任务名称
0, # 任务类型(0 表示普通任务)
"btpython /www/server/panel/class/panelModel/whole_machine_backupModel.py {} {} &> {}".format(
id, # 任务 ID
type, # 任务类型
self.logs_file # 日志文件路径
)
)
# 记录日志,表示任务创建成功
self.print_log("创建{}成功".format(name))
def input(self, get):
# 文件名清理:去掉干扰字符 (如 "(1)")
original_file_name = get.f_name
clean_file_name = original_file_name
if '(' in original_file_name and ')' in original_file_name:
clean_file_name = original_file_name.split('(')[0].strip() + ".tar.gz"
# file_name = '/www/backup/whole_machine_backup/{}'.format(get.f_name)
file_name = self.backup_path + clean_file_name
if not os.path.exists(self.backup_path):
os.makedirs(self.backup_path)
if clean_file_name in self.config:
return public.returnMsg(False, '已存在相同的任务,请先删除后再导入!')
from files import files
fileObj = files()
get.f_path = self.backup_path
ff = fileObj.upload(get)
if isinstance(ff, int):
return ff
if not ff['status']:
return ff
# 解压出全局配置文件
print(public.ExecShell('cd {} && tar -zxvf {} ./{} '.format(self.backup_path, clean_file_name, clean_file_name.rstrip('.tar.gz'))))
print(clean_file_name)
all_config_path = os.path.join(self.backup_path, clean_file_name.rstrip('.tar.gz'))
print(all_config_path)
time.sleep(0.2)
if not os.path.exists(all_config_path):
return public.returnMsg(False, '未找到配置文件!')
try:
res=json.loads(public.readFile(all_config_path))
res[clean_file_name.rstrip('.tar.gz')]['status'] = 1
self.config.update(res)
except Exception as e:
print(e)
return public.returnMsg(False, '配置文件解析失败!')
public.ExecShell('rm -rf {}'.format(all_config_path))
# 取出备份记录
if os.path.exists('{}backup.json'.format(self.backup_path)):
public.ExecShell('rm -rf {}backup.json'.format(self.backup_path))
public.ExecShell('cd {} && tar -zxvf {} ./backup.json'.format(self.backup_path, file_name))
time.sleep(0.1)
public.ExecShell('mv {}backup.json {}_backup.json'.format(self.backup_path, self.backup_path+clean_file_name.rstrip('.tar.gz')))
# 取出备份日志
if not os.path.exists(self.backup_log_path):
public.ExecShell('mkdir -p {}'.format(self.backup_log_path))
public.ExecShell('cd {} && tar -zxvf {} ./{}_backup.log'.format(self.backup_log_path, file_name,clean_file_name.rstrip('.tar.gz')))
public.writeFile(self.all_backup_config_path, json.dumps(self.config))
return public.returnMsg(True, '导入成功!')
def cp_config():
config_path = os.path.join(public.get_panel_path(), 'config/whole_machine_backup.json')
if not os.path.exists(config_path):
public.ExecShell("cp /www/backup/whole_machine_backup/whole_machine_backup.json {}".format(config_path))
cp_config()
del cp_config
if __name__ == '__main__':
id = sys.argv[1]
type = sys.argv[2]
main = main()
if type == '1':
print('1')
main.backup(id)
elif type == '2':
main.reduction(id)
elif type == '3':
main.check_mysql_files(sys.argv[3], sys.argv[4])
else:
print('参数错误!')
sys.exit(1)