Initial YakPanel commit
This commit is contained in:
0
mod/project/backup_restore/__init__.py
Normal file
0
mod/project/backup_restore/__init__.py
Normal file
616
mod/project/backup_restore/backup_manager.py
Normal file
616
mod/project/backup_restore/backup_manager.py
Normal file
@@ -0,0 +1,616 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <wzz@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from mod.project.backup_restore.modules.site_module import SiteModule
|
||||
from mod.project.backup_restore.modules.database_module import DatabaseModule
|
||||
from mod.project.backup_restore.modules.ftp_module import FtpModule
|
||||
from mod.project.backup_restore.modules.crontab_module import CrontabModule
|
||||
from mod.project.backup_restore.modules.ssh_module import SshModule
|
||||
from mod.project.backup_restore.modules.firewall_module import FirewallModule
|
||||
from mod.project.backup_restore.modules.plugin_module import PluginModule
|
||||
from mod.project.backup_restore.modules.mail_module import MailModule
|
||||
from mod.project.backup_restore.modules.ssl_model import SSLModel
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class BackupManager(SiteModule, DatabaseModule, FtpModule, SSLModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.backup_log_file = self.base_path + '/backup.log'
|
||||
self.backup_pl_file = self.base_path + '/backup.pl'
|
||||
self.backup_success_file = self.base_path + '/success.pl'
|
||||
self.backup_save_config = self.base_path + '/backup_save_config.json'
|
||||
self.history_log_path = '/www/backup/backup_restore/history/log'
|
||||
self.history_info_path = '/www/backup/backup_restore/history/info'
|
||||
self.migrate_backup_info_path = '/www/backup/backup_restore/migrate_backup_info.json'
|
||||
|
||||
def get_local_backup(self, get=None):
|
||||
backup_list = []
|
||||
if os.path.exists(self.bakcup_task_json):
|
||||
backup_list = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
|
||||
file_names = os.listdir(self.base_path)
|
||||
pattern = re.compile(r"\d{8}-\d{4}_\d+_backup\.tar\.gz")
|
||||
matched_files = [f for f in file_names if pattern.match(f)]
|
||||
for file in matched_files:
|
||||
if "upload.tmp" in file:
|
||||
continue
|
||||
file_timestamp = file.split('_')[1]
|
||||
matched = any(item["timestamp"] == int(file_timestamp) for item in backup_list)
|
||||
if not matched:
|
||||
done_time = datetime.datetime.fromtimestamp(int(file_timestamp)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
# file_size = public.ExecShell("du -sb /www/backup/backup_restore/{}".format(file))[0].split("\t")[0]
|
||||
file_conf = {
|
||||
'backup_name': str(file),
|
||||
'timestamp': int(file_timestamp),
|
||||
'create_time': done_time,
|
||||
'backup_time': done_time,
|
||||
'backup_file': self.base_path + "/" + file,
|
||||
'storage_type': "local",
|
||||
'auto_exit': 0,
|
||||
'restore_status': 0,
|
||||
'backup_status': 2,
|
||||
'backup_path': self.base_path + "/" + file,
|
||||
'done_time': done_time,
|
||||
'backup_file_size': str(self.get_file_size(self.base_path + "/" + file)),
|
||||
'backup_file_sha256': self.get_file_sha256(self.base_path + "/" + file),
|
||||
'backup_count': {
|
||||
"success": None,
|
||||
"failed": None,
|
||||
"total_time": None
|
||||
},
|
||||
}
|
||||
backup_list.append(file_conf)
|
||||
|
||||
if os.path.exists(self.migrate_backup_info_path):
|
||||
migrate_backup_info = json.loads(public.ReadFile(self.migrate_backup_info_path))
|
||||
backup_list.append(migrate_backup_info)
|
||||
public.ExecShell("rm -f {}".format(self.migrate_backup_info_path))
|
||||
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(backup_list))
|
||||
return backup_list
|
||||
|
||||
def get_backup_file_msg(self, timestamp):
|
||||
import tarfile
|
||||
backup_file = str(timestamp) + "_backup.tar.gz"
|
||||
print(backup_file)
|
||||
file_names = os.listdir(self.base_path)
|
||||
for file in file_names:
|
||||
if backup_file in file:
|
||||
backup_file = file
|
||||
path = self.base_path + "/" + backup_file
|
||||
path_data = {}
|
||||
if not os.path.exists(path):
|
||||
return path_data
|
||||
try:
|
||||
with tarfile.open(path, 'r:gz') as tar:
|
||||
# 提前获取文件列表
|
||||
members = tar.getnames()
|
||||
# 提取备份 JSON 配置
|
||||
if '{}_backup/backup.json'.format(timestamp) in members:
|
||||
json_file_name = '{}_backup/backup.json'.format(timestamp)
|
||||
json_file = tar.extractfile(json_file_name)
|
||||
json_content = json_file.read().decode('utf-8')
|
||||
path_data['config'] = json.loads(json_content)
|
||||
|
||||
# 提取备份日志文件
|
||||
if '{}_backup/backup.log'.format(timestamp) in members:
|
||||
log_file_name = '{}_backup/backup.log'.format(timestamp)
|
||||
log_file = tar.extractfile(log_file_name)
|
||||
log_content = log_file.read().decode('utf-8')
|
||||
path_data['log'] = log_content + path + "\n" + public.lang("Packaging completed")
|
||||
except:
|
||||
return False
|
||||
|
||||
# path_data['server_config']=self.get_server_config()
|
||||
# path_data['backup_path_size']=25256044
|
||||
# path_data['free_size'] = self.get_free_space()['free_space']
|
||||
|
||||
self.history_log_path = '/www/backup/backup_restore/history/log'
|
||||
self.history_info_path = '/www/backup/backup_restore/history/info'
|
||||
if not os.path.exists(self.history_log_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_log_path))
|
||||
if not os.path.exists(self.history_info_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_info_path))
|
||||
|
||||
try:
|
||||
public.WriteFile(self.history_log_path + "{}_backup.log".format(timestamp), path_data['log'])
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
public.WriteFile(self.history_info_path + "/{}_backup.info".format(timestamp),
|
||||
json.dumps(path_data['config']))
|
||||
except:
|
||||
return False
|
||||
|
||||
try:
|
||||
backup_task_info = self.get_backup_conf(timestamp)
|
||||
hitory_info = json.loads(public.ReadFile(self.history_info_path + "/{}_backup.info".format(timestamp)))
|
||||
hitory_info['create_time'] = backup_task_info['create_time']
|
||||
hitory_info['backup_time'] = backup_task_info['backup_time']
|
||||
hitory_info['backup_file'] = backup_task_info['backup_file']
|
||||
hitory_info['backup_path'] = backup_task_info['backup_path']
|
||||
hitory_info['done_time'] = backup_task_info['done_time']
|
||||
hitory_info['total_time'] = backup_task_info['total_time']
|
||||
hitory_info['backup_file_size'] = backup_task_info['backup_file_size']
|
||||
hitory_info['backup_file_sha256'] = backup_task_info['backup_file_sha256']
|
||||
public.WriteFile(self.history_info_path + "/{}_backup.info".format(timestamp), json.dumps(hitory_info))
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def add_backup_task(self, timestamp: int):
|
||||
"""
|
||||
构造备份初始配置
|
||||
"""
|
||||
backup_path = self.base_path + '/{timestamp}_backup/'.format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
os.makedirs(backup_path)
|
||||
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
if not backup_conf:
|
||||
print(public.lang("Backup configuration file does not exist"))
|
||||
return public.returnMsg(False, public.lang("Backup configuration file does not exist"))
|
||||
|
||||
backup_conf['data_list'] = {}
|
||||
backup_conf['data_list']['soft'] = self.get_soft_data(timestamp, packet=True)
|
||||
backup_conf['data_list']['site'] = self.get_site_backup_conf(timestamp)
|
||||
backup_conf['data_list']['ssl'] = self.get_ssl_backup_conf(timestamp)
|
||||
backup_conf['data_list']['database'] = self.get_database_backup_conf(timestamp)
|
||||
backup_conf['data_list']['ftp'] = self.backup_ftp_data(timestamp)
|
||||
backup_conf['backup_status'] = 1
|
||||
public.WriteFile(backup_path + 'backup.json', json.dumps(backup_conf))
|
||||
|
||||
def backup_data(self, timestamp: int):
|
||||
if os.path.exists(self.backup_log_file):
|
||||
public.ExecShell("rm -f {}".format(self.backup_log_file))
|
||||
|
||||
if os.path.exists(self.backup_pl_file):
|
||||
print(public.lang("A backup process is already running!"))
|
||||
return public.returnMsg(False, public.lang("A backup process is already running!"))
|
||||
|
||||
try:
|
||||
public.WriteFile(self.backup_pl_file, timestamp)
|
||||
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
backup_conf['backup_status'] = 1
|
||||
self.save_backup_conf(timestamp, backup_conf)
|
||||
start_time = int(time.time())
|
||||
# 构造备份初始配置
|
||||
self.add_backup_task(timestamp)
|
||||
try:
|
||||
self.backup_site_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.backup_database_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.backup_ssl_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
CrontabModule().backup_crontab_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
# TODO: 存在问题,下个版本修复
|
||||
# try:
|
||||
# SshModule().backup_ssh_data(timestamp)
|
||||
# except:
|
||||
# pass
|
||||
|
||||
try:
|
||||
FirewallModule().backup_firewall_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
MailModule().backup_vmail_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
PluginModule().backup_plugin_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.write_backup_data(timestamp)
|
||||
except:
|
||||
pass
|
||||
|
||||
end_time = int(time.time())
|
||||
done_time = datetime.datetime.fromtimestamp(int(end_time)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
total_time = end_time - start_time
|
||||
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
backup_conf['backup_status'] = 2
|
||||
backup_conf['done_time'] = done_time
|
||||
backup_conf['total_time'] = total_time
|
||||
|
||||
self.save_backup_conf(timestamp, backup_conf)
|
||||
self.sync_backup_info(timestamp)
|
||||
|
||||
public.WriteFile(self.backup_success_file, timestamp)
|
||||
public.ExecShell("rm -f {}".format(self.backup_pl_file))
|
||||
self.create_history_file(timestamp)
|
||||
except Exception as e:
|
||||
return public.returnMsg(False, public.lang(f"something went wrong! Error: {str(e)}"))
|
||||
finally:
|
||||
if os.path.exists(self.backup_pl_file):
|
||||
public.ExecShell("rm -f {}".format(self.backup_pl_file))
|
||||
|
||||
def create_history_file(self, timestamp):
|
||||
if not os.path.exists(self.history_log_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_log_path))
|
||||
if not os.path.exists(self.history_info_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_info_path))
|
||||
|
||||
hitory_log_file = self.history_log_path + '/' + str(timestamp) + '_backup.log'
|
||||
history_info_file = self.history_info_path + '/' + str(timestamp) + '_backup.info'
|
||||
public.WriteFile(hitory_log_file, public.ReadFile("/www/backup/backup_restore/backup.log".format(timestamp)))
|
||||
public.WriteFile(history_info_file,
|
||||
public.ReadFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp)))
|
||||
|
||||
def sync_backup_info(self, timestamp):
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
data_list['backup_status'] = backup_conf['backup_status']
|
||||
data_list['backup_file'] = backup_conf['backup_file']
|
||||
data_list['backup_file_sha256'] = backup_conf['backup_file_sha256']
|
||||
data_list['backup_file_size'] = backup_conf['backup_file_size']
|
||||
data_list['done_time'] = backup_conf['done_time']
|
||||
data_list['total_time'] = backup_conf['total_time']
|
||||
data_list['backup_count'] = backup_conf['backup_count']
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
def count_backup_status(self, data, status_code):
|
||||
return sum(
|
||||
1 for category in data.values()
|
||||
for item in category
|
||||
if isinstance(item, dict) and item.get('status') == status_code
|
||||
)
|
||||
|
||||
def write_backup_data(self, timestamp):
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Start compressing and packaging all data"), "backup")
|
||||
from datetime import datetime
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
|
||||
backup_log_path = self.base_path + str(timestamp) + "_backup/"
|
||||
public.ExecShell('\cp -rpa {} {}'.format(self.backup_log_file, backup_log_path))
|
||||
|
||||
conf_data = json.loads((public.ReadFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp))))
|
||||
status_2_count = self.count_backup_status(conf_data['data_list'], 2)
|
||||
status_3_count = self.count_backup_status(conf_data['data_list'], 3)
|
||||
|
||||
dt_object = datetime.fromtimestamp(int(timestamp))
|
||||
file_time = dt_object.strftime('%Y%m%d-%H%M')
|
||||
tar_file_name = file_time + "_" + str(timestamp) + "_backup.tar.gz"
|
||||
conf_data['backup_status'] = 1
|
||||
public.WriteFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp), json.dumps(conf_data))
|
||||
|
||||
public.ExecShell("cd /www/backup/backup_restore && tar -czvf {} {}_backup".format(tar_file_name, timestamp))
|
||||
file_size = public.ExecShell("du -sb /www/backup/backup_restore/{}".format(tar_file_name))[0].split("\t")[0]
|
||||
|
||||
backup_conf["backup_status"] = 2
|
||||
backup_conf["backup_file"] = "/www/backup/backup_restore/" + tar_file_name
|
||||
backup_conf["backup_file_sha256"] = self.get_file_sha256("/www/backup/backup_restore/" + tar_file_name)
|
||||
backup_conf["backup_file_size"] = file_size
|
||||
backup_conf["backup_count"] = {}
|
||||
backup_conf["backup_count"]['success'] = status_2_count
|
||||
backup_conf["backup_count"]['failed'] = status_3_count
|
||||
storage_type = backup_conf['storage_type']
|
||||
|
||||
backup_size = self.format_size(int(file_size))
|
||||
self.print_log(
|
||||
public.lang("Compression and packaging of all data completed. Data size: {}").format(backup_size),
|
||||
'backup'
|
||||
)
|
||||
self.print_log(public.lang("Backup completed. Backup file: {}").format(tar_file_name), "backup")
|
||||
self.print_log("====================================================", "backup")
|
||||
|
||||
tar_file_name = "/www/backup/backup_restore/" + tar_file_name
|
||||
if storage_type != "local" and os.path.exists(tar_file_name):
|
||||
cloud_name_cn = "cloud storage"
|
||||
self.print_log(public.lang("Uploading backup file to cloud storage..."), "backup")
|
||||
try:
|
||||
from cloud_stora_upload_v2 import CloudStoraUpload
|
||||
_cloud = CloudStoraUpload()
|
||||
_cloud.run(storage_type)
|
||||
cloud_name_cn = _cloud.obj._title
|
||||
if int(file_size) > 100 * 1024 * 1024:
|
||||
self.print_log(
|
||||
public.lang("{} Uploading in chunks...").format(cloud_name_cn), "backup"
|
||||
)
|
||||
else:
|
||||
self.print_log(
|
||||
public.lang("{} Uploading...").format(cloud_name_cn), "backup"
|
||||
)
|
||||
|
||||
backup_path = _cloud.obj.backup_path
|
||||
if not backup_path.endswith('/'):
|
||||
backup_path += '/'
|
||||
upload_path = os.path.join(backup_path, "backup_restore", os.path.basename(tar_file_name))
|
||||
if _cloud.cloud_upload_file(tar_file_name, upload_path):
|
||||
self.print_log(public.lang("Successfully uploaded to {}").format(cloud_name_cn), "backup")
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
self.print_log(
|
||||
public.lang("Error occurred while uploading to {}: {}").format(cloud_name_cn, str(e)),
|
||||
"backup"
|
||||
)
|
||||
|
||||
self.save_backup_conf(timestamp, backup_conf)
|
||||
|
||||
def get_backup_details(self, timestamp):
|
||||
history_info_file = self.history_info_path + '/' + str(timestamp) + '_backup.info'
|
||||
if not os.path.exists(history_info_file):
|
||||
get_info = self.get_backup_file_msg(timestamp)
|
||||
if not get_info:
|
||||
return public.fail_v2(public.lang("Backup info not found"))
|
||||
|
||||
backup_info = json.loads(public.ReadFile(history_info_file))
|
||||
result = self.process_detail(backup_info)
|
||||
return public.success_v2(result)
|
||||
|
||||
def get_backup_log(self, timestamp):
|
||||
backup_log_file = self.base_path + '/backup.log'
|
||||
history_log_file = self.history_log_path + '/' + str(timestamp) + '_backup.log'
|
||||
if os.path.exists(self.backup_pl_file):
|
||||
backup_timestamp = int(public.ReadFile(self.backup_pl_file))
|
||||
if int(backup_timestamp) == int(timestamp):
|
||||
return public.ReadFile(backup_log_file)
|
||||
if os.path.exists(history_log_file):
|
||||
return public.ReadFile(history_log_file)
|
||||
else:
|
||||
return None
|
||||
|
||||
# todo 弃用
|
||||
def get_backup_progress(self, get=None):
|
||||
"""
|
||||
获取备份进度信息
|
||||
@param get: object 包含请求参数
|
||||
@return: dict 备份进度信息
|
||||
"""
|
||||
# 设置相关文件路径
|
||||
backup_pl_file = self.base_path + '/backup.pl'
|
||||
backup_log_file = self.base_path + '/backup.log'
|
||||
backup_success_file = self.base_path + '/success.pl'
|
||||
|
||||
# 创建处理已完成备份的函数,减少代码重复
|
||||
def create_completed_result(backup_timestamp):
|
||||
if not backup_timestamp:
|
||||
return public.ReturnMsg(False, public.lang("Backup completed but unable to retrieve timestamp"))
|
||||
|
||||
if not os.path.exists(self.bakcup_task_json):
|
||||
return public.ReturnMsg(False, public.lang("Backup configuration file does not exist"))
|
||||
|
||||
backup_configs = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
success_data = next(
|
||||
(item for item in backup_configs if str(item.get('timestamp')) == str(backup_timestamp)), {}
|
||||
)
|
||||
return {
|
||||
"task_type": "backup",
|
||||
"task_status": 2,
|
||||
"backup_data": None,
|
||||
"backup_name": None,
|
||||
"data_backup_status": 2,
|
||||
"progress": 100,
|
||||
"msg": None,
|
||||
'exec_log': public.ReadFile(backup_log_file) if os.path.exists(backup_log_file) else "",
|
||||
'timestamp': backup_timestamp,
|
||||
'backup_file_info': success_data,
|
||||
'err_info': []
|
||||
}
|
||||
|
||||
# 检查备份是否已完成
|
||||
if os.path.exists(backup_success_file):
|
||||
success_time = int(os.path.getctime(backup_success_file))
|
||||
local_time = int(time.time())
|
||||
# 如果success文件创建时间在10秒内,说明备份刚刚完成
|
||||
if success_time + 10 > local_time:
|
||||
try:
|
||||
backup_timestamp = public.ReadFile(backup_success_file).strip()
|
||||
return public.ReturnMsg(True, create_completed_result(backup_timestamp))
|
||||
except Exception as e:
|
||||
public.ExecShell("rm -f {}".format(backup_success_file))
|
||||
return public.ReturnMsg(False,
|
||||
public.lang("Error retrieving backup completion information: {}").format(
|
||||
str(e)))
|
||||
else:
|
||||
# 超过10秒,删除success文件
|
||||
public.ExecShell("rm -f {}".format(backup_success_file))
|
||||
|
||||
# 检查是否有备份进程运行
|
||||
try:
|
||||
# 检查备份进程锁文件
|
||||
if os.path.exists(backup_pl_file):
|
||||
timestamp = public.ReadFile(backup_pl_file).strip()
|
||||
if not timestamp:
|
||||
return public.ReturnMsg(False,
|
||||
public.lang("Backup process is running, but unable to retrieve timestamp"))
|
||||
else:
|
||||
# 等待2秒,可能是备份刚刚完成
|
||||
time.sleep(2)
|
||||
if os.path.exists(backup_success_file):
|
||||
success_time = int(os.path.getctime(backup_success_file))
|
||||
local_time = int(time.time())
|
||||
if success_time + 10 > local_time:
|
||||
backup_timestamp = public.ReadFile(backup_success_file).strip()
|
||||
return public.ReturnMsg(True, create_completed_result(backup_timestamp))
|
||||
|
||||
# 再次检查是否有备份进程
|
||||
if os.path.exists(backup_pl_file):
|
||||
timestamp = public.ReadFile(backup_pl_file).strip()
|
||||
if not timestamp:
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"Backup process is running, but unable to retrieve timestamp"))
|
||||
else:
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"No ongoing backup tasks found. Please check the backup list to see if the backup is completed"))
|
||||
|
||||
# 读取备份配置文件
|
||||
backup_json_path = f"{self.base_path}/{timestamp}_backup/backup.json"
|
||||
count = 0
|
||||
while 1:
|
||||
if count >= 3:
|
||||
return public.ReturnMsg(False, public.lang("Backup configuration file does not exist: {}").format(
|
||||
backup_json_path))
|
||||
count += 1
|
||||
if not os.path.exists(backup_json_path):
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
|
||||
conf_data = json.loads(public.ReadFile(backup_json_path))
|
||||
except Exception as e:
|
||||
return public.ReturnMsg(False,
|
||||
public.lang("Error retrieving backup progress information: {}").format(str(e)))
|
||||
|
||||
# 读取备份日志
|
||||
backup_log_data = public.ReadFile(backup_log_file) if os.path.exists(backup_log_file) else ""
|
||||
|
||||
# 定义备份类型及其处理逻辑
|
||||
backup_types = [
|
||||
{
|
||||
'type': 'site',
|
||||
'data_key': 'site',
|
||||
'display_name': 'site',
|
||||
'progress': 30
|
||||
},
|
||||
{
|
||||
'type': 'database',
|
||||
'data_key': 'database',
|
||||
'display_name': 'database',
|
||||
'progress': 60
|
||||
},
|
||||
{
|
||||
'type': 'ftp',
|
||||
'data_key': 'ftp',
|
||||
'display_name': 'ftp',
|
||||
'progress': 70
|
||||
},
|
||||
{
|
||||
'type': 'terminal',
|
||||
'data_key': 'terminal',
|
||||
'display_name': 'terminal',
|
||||
'progress': 75
|
||||
},
|
||||
{
|
||||
'type': 'firewall',
|
||||
'data_key': 'firewall',
|
||||
'display_name': 'firewall',
|
||||
'progress': 90
|
||||
}
|
||||
]
|
||||
|
||||
# 检查各类型备份进度
|
||||
for backup_type in backup_types:
|
||||
items = conf_data.get("data_list", {}).get(backup_type['data_key'], [])
|
||||
for item in items:
|
||||
try:
|
||||
if item.get("status") == 2:
|
||||
continue
|
||||
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "backup",
|
||||
"task_status": 1,
|
||||
"data_type": backup_type['type'],
|
||||
"name": item.get("name", f"unknow {backup_type['display_name']}"),
|
||||
"data_backup_status": item.get("status", 0),
|
||||
"progress": backup_type['progress'],
|
||||
"msg": item.get("msg"),
|
||||
'exec_log': backup_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
except:
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "backup",
|
||||
"task_status": 1,
|
||||
"data_type": public.lang("Server Configuration"),
|
||||
"name": public.lang("Server Configuration"),
|
||||
"data_backup_status": 1,
|
||||
"progress": 80,
|
||||
"msg": public.lang("Backing up server configuration"),
|
||||
'exec_log': backup_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
|
||||
# 检查数据打包进度
|
||||
try:
|
||||
backup_status = conf_data.get('backup_status')
|
||||
if backup_status == 1:
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "backup",
|
||||
"task_status": 1,
|
||||
"data_type": "tar",
|
||||
"name": public.lang("Data Packaging"),
|
||||
"data_backup_status": 1,
|
||||
"progress": 90,
|
||||
'exec_log': backup_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
except Exception:
|
||||
# 可能没有backup_status字段,继续处理
|
||||
pass
|
||||
|
||||
# 如果没有发现进行中的任务,但有备份进程
|
||||
if timestamp:
|
||||
return {
|
||||
"backup_data": "unknown",
|
||||
"backup_name": "unknow",
|
||||
"data_backup_status": 1,
|
||||
"progress": 10,
|
||||
'backup_msg': public.lang("Preparing backup data"),
|
||||
'backup_log': backup_log_data,
|
||||
'timestamp': timestamp
|
||||
}
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"No ongoing backup tasks found. Please check the backup list to see if the backup is completed"))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # IP地址
|
||||
backup_manager = BackupManager() # 实例化对象
|
||||
if hasattr(backup_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(backup_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' does not exist")
|
||||
112
mod/project/backup_restore/base_util.py
Normal file
112
mod/project/backup_restore/base_util.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <wzz@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
import public
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class BaseUtil:
|
||||
def __init__(self):
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
if not os.path.exists(self.base_path):
|
||||
public.ExecShell('mkdir -p {}'.format(self.base_path))
|
||||
self.history_path = '/www/backup/backup_restore/history'
|
||||
self.history_info_path = '/www/backup/backup_restore/history/info'
|
||||
self.nginx_bin_path = '/www/server/nginx/sbin/nginx'
|
||||
self.overwrite = 0 # 0 跳过, 1 覆盖
|
||||
self.auto_exit = 0 # 异常打断, 默认0
|
||||
|
||||
def print_log(self, log: str, type: str):
|
||||
time_str = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
log = "[{}] {}".format(time_str, log)
|
||||
log_file = self.base_path + '/{}.log'.format(type)
|
||||
public.writeFile(log_file, log + "\n", 'a+')
|
||||
|
||||
def replace_log(self, old_str: str, new_str: str, type: str):
|
||||
log_file = self.base_path + '/{}.log'.format(type)
|
||||
log_data = public.ReadFile(log_file)
|
||||
if old_str in log_data:
|
||||
log_data = log_data.replace(old_str, new_str)
|
||||
public.WriteFile(log_file, log_data)
|
||||
|
||||
def get_file_sha256(self, file_path) -> str:
|
||||
sha256 = hashlib.sha256()
|
||||
with open(file_path, "rb") as f:
|
||||
while True:
|
||||
chunk = f.read(4096) # 先读取再判断
|
||||
if not chunk:
|
||||
break
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
def get_free_space(self):
|
||||
result = {}
|
||||
path = "/www"
|
||||
diskstat = os.statvfs(path)
|
||||
free_space = diskstat.f_bavail * diskstat.f_frsize
|
||||
# total_space = diskstat.f_blocks * diskstat.f_frsize
|
||||
# used_space = (diskstat.f_blocks - diskstat.f_bfree) * diskstat.f_frsize
|
||||
result['free_space'] = free_space
|
||||
return result
|
||||
|
||||
def get_file_size(self, path: str) -> int:
|
||||
try:
|
||||
if os.path.isfile(path):
|
||||
return os.path.getsize(path)
|
||||
elif os.path.isdir(path):
|
||||
return int(public.ExecShell(f"du -sb {path}")[0].split("\t")[0])
|
||||
return 0
|
||||
except:
|
||||
return 0
|
||||
|
||||
def format_size(self, size: int):
|
||||
if size < 1024:
|
||||
return f"{size}B"
|
||||
elif size < 1024 * 1024:
|
||||
return f"{size / 1024:.2f}KB"
|
||||
elif size < 1024 * 1024 * 1024:
|
||||
return f"{size / 1024 / 1024:.2f}MB"
|
||||
elif size < 1024 * 1024 * 1024 * 1024:
|
||||
return f"{size / 1024 / 1024 / 1024:.2f}GB"
|
||||
else:
|
||||
return f"{size / 1024 / 1024 / 1024 / 1024:.2f}TB"
|
||||
|
||||
def web_config_check(self):
|
||||
if os.path.exists(self.nginx_bin_path):
|
||||
nginx_conf_test = public.ExecShell("ulimit -n 8192 ;{} -t".format(self.nginx_bin_path))[1]
|
||||
if "successful" in nginx_conf_test:
|
||||
return {
|
||||
"status": True,
|
||||
"msg": None
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": False,
|
||||
"msg": public.lang("Nginx Configuration file error, inventory!:{}".format(nginx_conf_test))
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": True,
|
||||
"msg": None
|
||||
}
|
||||
691
mod/project/backup_restore/comMod.py
Normal file
691
mod/project/backup_restore/comMod.py
Normal file
@@ -0,0 +1,691 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <wzz@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
import public.validate
|
||||
from public import Param
|
||||
from public.exceptions import HintException
|
||||
from mod.project.backup_restore.data_manager import DataManager
|
||||
from mod.project.backup_restore.backup_manager import BackupManager
|
||||
from mod.project.backup_restore.restore_manager import RestoreManager
|
||||
from mod.project.backup_restore.ssh_manager import BtInstallManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class main(DataManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.backup_pl_file = self.base_path + '/backup.pl'
|
||||
self.restore_pl_file = self.base_path + '/restore.pl'
|
||||
self.migrate_task_json = self.base_path + '/migration_task.json'
|
||||
self.migrate_pl_file = self.base_path + '/migrate.pl'
|
||||
self.migrate_success_pl = self.base_path + '/migrate_success.pl'
|
||||
|
||||
def return_data(self, status: bool = None, msg: str = None, error_msg: str = None, data: list | dict = None):
|
||||
aa_status = 0 if status else -1
|
||||
result = None
|
||||
if not isinstance(data, type(None)):
|
||||
result = data
|
||||
elif msg:
|
||||
result = public.lang(msg)
|
||||
elif error_msg:
|
||||
result = public.lang(error_msg)
|
||||
return public.return_message(aa_status, 0, result)
|
||||
|
||||
def add_backup(self, get):
|
||||
""" 备份"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("backup_name").String().Require(),
|
||||
Param("storage_type").String().Require(),
|
||||
Param("timestamp").Integer().Require(),
|
||||
Param("auto_exit").Integer("in", [0, 1]).Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
if os.path.exists(self.backup_pl_file):
|
||||
self.task_stop()
|
||||
|
||||
if os.path.exists(self.base_path + "/success.pl"):
|
||||
try:
|
||||
if int(os.path.getctime(self.base_path + "/success.pl")) + 10 > int(time.time()):
|
||||
return public.fail_v2(public.lang("Please do not operate frequently, please wait a moment"))
|
||||
except:
|
||||
pass
|
||||
|
||||
web_check = self.web_config_check()
|
||||
if web_check['status'] is False:
|
||||
return self.return_data(error_msg=web_check['msg'])
|
||||
|
||||
backup_config = []
|
||||
if os.path.exists(self.bakcup_task_json):
|
||||
backup_config = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
|
||||
get.auto_exit = 0 # 强制不打断, 通过error msg交互
|
||||
backup_now = False
|
||||
if not hasattr(get, "timestamp"):
|
||||
get_time = ""
|
||||
else:
|
||||
try:
|
||||
get_time = int(get.timestamp)
|
||||
except:
|
||||
get_time = get.timestamp
|
||||
|
||||
local_timestamp = int(time.time())
|
||||
if get_time == "" or get_time == "0" or get_time == 0:
|
||||
backup_timestamp = local_timestamp
|
||||
get_time = local_timestamp
|
||||
backup_now = True
|
||||
else:
|
||||
backup_timestamp = get_time
|
||||
|
||||
backup_conf = {
|
||||
'backup_name': get.backup_name,
|
||||
'timestamp': get_time,
|
||||
'create_time': datetime.datetime.fromtimestamp(int(local_timestamp)).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'backup_time': datetime.datetime.fromtimestamp(int(backup_timestamp)).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'storage_type': get.storage_type,
|
||||
'auto_exit': int(get.auto_exit),
|
||||
'backup_status': 0 if not backup_now else 1,
|
||||
'restore_status': 0,
|
||||
'backup_path': self.base_path + "/" + str(get_time) + "_backup",
|
||||
'backup_file': "",
|
||||
'backup_file_sha256': "",
|
||||
'backup_file_size': "",
|
||||
'backup_count': {
|
||||
'success': None,
|
||||
'failed': None,
|
||||
},
|
||||
'total_time': None,
|
||||
'done_time': None,
|
||||
}
|
||||
backup_config.append(backup_conf)
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(backup_config))
|
||||
|
||||
if backup_now:
|
||||
public.ExecShell(
|
||||
"nohup btpython /www/server/panel/mod/project/backup_restore/backup_manager.py backup_data {} > /dev/null 2>&1 &".format(
|
||||
int(get_time)
|
||||
)
|
||||
)
|
||||
else:
|
||||
# todo at time
|
||||
# 2024-05-20 14:00
|
||||
at_time_str = time.strftime("%Y-%m-%d %H:%M", time.localtime(int(get_time)))
|
||||
exec_script = "btpython /www/server/panel/mod/project/backup_restore/backup_manager.py"
|
||||
exec_command = f"cd {public.get_panel_path()} && echo 'nohup {exec_script} backup_data {int(get_time)} > /dev/null 2>&1' | at {at_time_str}"
|
||||
public.print_log(f"{exec_command}")
|
||||
public.ExecShell(exec_command)
|
||||
public.set_module_logs('backup_restore', 'add_backup', 1)
|
||||
return self.return_data(True, public.lang("Added successfully"))
|
||||
|
||||
def get_backup_list(self, get=None):
|
||||
if not os.path.exists(self.base_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.base_path))
|
||||
backup_config = BackupManager().get_local_backup()
|
||||
backup_config = sorted(backup_config, key=lambda x: int(x["timestamp"]), reverse=True)
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), None, backup_config)
|
||||
|
||||
def del_backup(self, get):
|
||||
if not hasattr(get, "timestamp"):
|
||||
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
|
||||
|
||||
backup_config = []
|
||||
if os.path.exists(self.bakcup_task_json):
|
||||
backup_config = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
|
||||
for backup_conf in backup_config:
|
||||
if backup_conf['timestamp'] == int(get.timestamp):
|
||||
for i in [
|
||||
backup_conf.get("backup_file", ""),
|
||||
backup_conf.get("backup_path", ""),
|
||||
]:
|
||||
if i and os.path.exists(i):
|
||||
public.ExecShell(f"rm -rf {i}")
|
||||
backup_config.remove(backup_conf)
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(backup_config))
|
||||
|
||||
info_path = os.path.join(self.base_path, "history", "info")
|
||||
log_path = os.path.join(self.base_path, "history", "log")
|
||||
if os.path.exists(info_path):
|
||||
for item in os.listdir(info_path):
|
||||
if item.startswith(str(get.timestamp)):
|
||||
public.ExecShell("rm -rf {}".format(os.path.join(info_path, item)))
|
||||
if os.path.exists(log_path):
|
||||
for item in os.listdir(log_path):
|
||||
if item.startswith(str(get.timestamp)):
|
||||
public.ExecShell("rm -rf {}".format(os.path.join(log_path, item)))
|
||||
|
||||
return self.return_data(True, public.lang("Deleted successfully"))
|
||||
|
||||
backup_file_list = os.listdir(self.base_path)
|
||||
for backup_file in backup_file_list:
|
||||
if backup_file.endswith(".tar.gz") or backup_file.endswith("_backup") or backup_file.endswith("_migration"):
|
||||
if str(get.timestamp) in backup_file:
|
||||
if os.path.exists(os.path.join(self.base_path, backup_file)):
|
||||
public.ExecShell("rm -rf {}".format(os.path.join(self.base_path, backup_file)))
|
||||
return self.return_data(True, public.lang("Deleted successfully"))
|
||||
|
||||
return self.return_data(False, public.lang("Deletion failed"))
|
||||
|
||||
def get_data_total(self, get=None):
|
||||
server_data = self.get_data_list()
|
||||
return self.return_data(status=True, data=server_data)
|
||||
|
||||
def get_progress(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("type").String(opt="in", length_or_list=["backup", "restore"]).Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
type = get.type
|
||||
progress_data = self.get_progress_with_type(type)
|
||||
if progress_data['status'] is True:
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), data=progress_data.get('msg'))
|
||||
|
||||
return self.return_data(False, error_msg=progress_data.get('msg', public.lang("Failed to get progress")))
|
||||
|
||||
def get_details(self, get):
|
||||
""" 获取备份或还原任务的详细信息"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("type").String(opt="in", length_or_list=["backup", "restore"]).Require(),
|
||||
Param("timestamp").Timestamp().Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
if get.type == "backup":
|
||||
return BackupManager().get_backup_details(get.timestamp)
|
||||
elif get.type == "restore":
|
||||
return RestoreManager().get_restore_details(get.timestamp)
|
||||
|
||||
raise HintException(public.lang("Unknown Type"))
|
||||
|
||||
def get_exec_logs(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("timestamp").Integer().Require(),
|
||||
Param("type").String("in", ["backup", "restore"]).Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
timestamp = get.timestamp
|
||||
type = get.type
|
||||
exec_logs = ""
|
||||
if type == "backup":
|
||||
exec_logs = BackupManager().get_backup_log(timestamp)
|
||||
elif type == "restore":
|
||||
exec_logs = RestoreManager().get_restore_log(timestamp)
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), "", exec_logs)
|
||||
|
||||
def task_stop(self, get=None):
|
||||
backup_task_pid = public.ExecShell(
|
||||
"ps -ef|grep 'backup_manager.py'|grep -v grep|awk '{print $2}'"
|
||||
)[0].replace("\n", "")
|
||||
if backup_task_pid:
|
||||
public.ExecShell("kill {}".format(backup_task_pid))
|
||||
|
||||
restore_task_pid = public.ExecShell(
|
||||
"ps -ef|grep 'restore_manager.py'|grep -v grep|awk '{print $2}'"
|
||||
)[0].replace("\n", "")
|
||||
if restore_task_pid:
|
||||
public.ExecShell("kill {}".format(restore_task_pid))
|
||||
|
||||
if os.path.exists(self.backup_pl_file):
|
||||
public.ExecShell("rm -f {}".format(self.backup_pl_file))
|
||||
|
||||
if os.path.exists(self.restore_pl_file):
|
||||
public.ExecShell("rm -f {}".format(self.restore_pl_file))
|
||||
|
||||
try:
|
||||
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
for item in task_json_data:
|
||||
if 'backup_status' in item and item['backup_status'] == 1:
|
||||
item['backup_status'] = 0
|
||||
if 'restore_status' in item and item['restore_status'] == 1:
|
||||
item['restore_status'] = 0
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(task_json_data))
|
||||
except:
|
||||
pass
|
||||
|
||||
if os.path.exists("/www/server/panel/data/migration.pl"):
|
||||
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
|
||||
return self.return_data(True, public.lang("Task stopped successfully"), None, None)
|
||||
|
||||
def get_backup_detail(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("timestamp").Integer().Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
timestamp = get.timestamp
|
||||
data = BackupManager().get_backup_file_msg(timestamp)
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), "", data)
|
||||
|
||||
def exec_backup(self, get=None):
|
||||
if not hasattr(get, "timestamp"):
|
||||
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
|
||||
timestamp = get.timestamp
|
||||
public.ExecShell(
|
||||
"nohup btpython /www/server/panel/mod/project/backup_restore/backup_manager.py backup_data {} > /dev/null 2>&1 &".format(
|
||||
int(timestamp)
|
||||
)
|
||||
)
|
||||
return self.return_data(True, public.lang("Executed successfully"), public.lang("Executed successfully"))
|
||||
|
||||
def add_restore(self, get=None):
|
||||
"""
|
||||
还原
|
||||
"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("timestamp").Integer().Require(),
|
||||
Param("auto_exit").Integer("in", [0, 1]).Require(), # 打断任务
|
||||
Param("force_restore").Integer("in", [0, 1]).Require(), # 覆盖强制还原
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
if os.path.exists(self.restore_pl_file):
|
||||
self.task_stop()
|
||||
|
||||
if os.path.exists(self.base_path + "/restore_success.pl"):
|
||||
try:
|
||||
if int(os.path.getctime(self.base_path + "/restore_success.pl")) + 10 > int(time.time()):
|
||||
return public.fail_v2(public.lang("Please do not operate frequently, please wait a moment"))
|
||||
except:
|
||||
pass
|
||||
|
||||
timestamp = get.timestamp
|
||||
|
||||
public.ExecShell(
|
||||
"nohup btpython /www/server/panel/mod/project/backup_restore/restore_manager.py restore_data {} {} > /dev/null 2>&1 &".format(
|
||||
int(timestamp), int(get.force_restore)
|
||||
)
|
||||
)
|
||||
public.set_module_logs('backup_restore', 'add_restore', 1)
|
||||
return self.return_data(True, public.lang("Restore task added successfully"))
|
||||
|
||||
def ssh_auth_check(self, get):
|
||||
"""验证SSH连接信息是否正常"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("server_ip").String().Require(),
|
||||
Param("ssh_port").Integer(),
|
||||
Param("ssh_user").String().Require(),
|
||||
Param("password").String(),
|
||||
Param("auth_type").String("in", ["password", "key"]).Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
web_check = self.web_config_check()
|
||||
if web_check['status'] is False:
|
||||
return self.return_data(error_msg="{}".format(web_check['msg']))
|
||||
|
||||
port = int(get.ssh_port) if hasattr(get, "ssh_port") and get.ssh_port else 22
|
||||
ssh_client = self.ssh_net_client_check(get.server_ip, port)
|
||||
if not ssh_client:
|
||||
return self.return_data(
|
||||
error_msg=public.lang("SSH connection test failed, please check if the IP and port are correct"))
|
||||
|
||||
password = None
|
||||
key_file = None
|
||||
# 至少需要提供密码或密钥文件之一
|
||||
if hasattr(get, "password") and get.password:
|
||||
password = get.password
|
||||
|
||||
if get.auth_type == "password":
|
||||
key_file = None
|
||||
elif get.auth_type == "key":
|
||||
key_file = "/www/backup/backup_restore/key_file"
|
||||
public.WriteFile(key_file, get.password)
|
||||
public.ExecShell("chmod 600 {}".format(key_file))
|
||||
|
||||
# 创建SSH管理器实例并验证连接
|
||||
manager = BtInstallManager(
|
||||
host=get.server_ip,
|
||||
port=port,
|
||||
username=get.ssh_user,
|
||||
password=password,
|
||||
key_file=key_file
|
||||
)
|
||||
|
||||
result = manager.verify_ssh_connection()
|
||||
if result["status"]:
|
||||
return self.return_data(True, public.lang("SSH connection verified successfully"), None, None)
|
||||
return self.return_data(error_msg=result["msg"])
|
||||
|
||||
def add_migrate_task(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("server_ip").String().Require(),
|
||||
Param("ssh_port").Integer(),
|
||||
Param("ssh_user").String().Require(),
|
||||
Param("password").String(),
|
||||
Param("auth_type").String("in", ["password", "key"]).Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
self.stop_migrate()
|
||||
if os.path.exists("/www/backup/backup_restore/migration.log"):
|
||||
public.ExecShell("rm -f /www/backup/backup_restore/migration.log")
|
||||
|
||||
server_ip = get.server_ip
|
||||
ssh_port = get.ssh_port
|
||||
ssh_user = get.ssh_user
|
||||
auth_type = get.auth_type
|
||||
password = get.password
|
||||
|
||||
if auth_type == "key":
|
||||
key_file = "/www/backup/backup_restore/key_file"
|
||||
public.WriteFile(key_file, password)
|
||||
public.ExecShell("chmod 600 {}".format(key_file))
|
||||
else:
|
||||
key_file = None
|
||||
|
||||
timestamp = int(time.time())
|
||||
migrate_conf = {
|
||||
'server_ip': server_ip,
|
||||
'ssh_port': ssh_port,
|
||||
'ssh_user': ssh_user,
|
||||
'auth_type': auth_type,
|
||||
'password': password,
|
||||
'timestamp': timestamp,
|
||||
'run_type': "INIT",
|
||||
'run_status': 1,
|
||||
'confirm': 0,
|
||||
'step': 1,
|
||||
'migrate_progress': 5,
|
||||
'migrate_msg': public.lang("Migration task initializing"),
|
||||
'task_info': None,
|
||||
}
|
||||
public.WriteFile(self.migrate_task_json, json.dumps(migrate_conf))
|
||||
|
||||
if auth_type == "password":
|
||||
public.ExecShell(
|
||||
"nohup btpython /www/server/panel/mod/project/backup_restore/ssh_manager.py --action migrate -H {server_ip} -P {ssh_port} -u {ssh_user} --password='{password}' --task-name '{task_name}' > /dev/null 2>&1 &".format(
|
||||
server_ip=server_ip, ssh_port=ssh_port, ssh_user=ssh_user, password=password,
|
||||
task_name=public.lang("My Migration Task")
|
||||
)
|
||||
)
|
||||
elif auth_type == "key":
|
||||
public.ExecShell(
|
||||
"nohup btpython /www/server/panel/mod/project/backup_restore/ssh_manager.py --action migrate -H {server_ip} -P {ssh_port} -u {ssh_user} --key-file {key_file} --task-name '{task_name}' > /dev/null 2>&1 &".format(
|
||||
server_ip=server_ip, ssh_port=ssh_port, ssh_user=ssh_user, key_file=key_file,
|
||||
task_name=public.lang("My Migration Task")
|
||||
)
|
||||
)
|
||||
public.set_module_logs('backup_restore', 'add_migrate_task', 1)
|
||||
return self.return_data(True, public.lang("Migration task added successfully"), None, None)
|
||||
|
||||
def get_migrate_status(self, get=None):
|
||||
if os.path.exists(self.migrate_task_json):
|
||||
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
|
||||
result = {
|
||||
"server_ip": migrate_config.get('server_ip', ''),
|
||||
"ssh_port": migrate_config.get('ssh_port', 22),
|
||||
"ssh_user": migrate_config.get('ssh_user', ''),
|
||||
"auth_type": migrate_config.get('auth_type', 'password'),
|
||||
"password": migrate_config.get('password', ''),
|
||||
"migrate_progress": migrate_config.get('migrate_progress', 0),
|
||||
"timestamp": migrate_config.get("timestamp", 0),
|
||||
"total_time": migrate_config.get("total_time", 0),
|
||||
"is_running": migrate_config['run_status'] == 1 or migrate_config.get("confirm", 0) == 0,
|
||||
}
|
||||
else:
|
||||
result = {
|
||||
"is_running": False,
|
||||
}
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), None, result)
|
||||
|
||||
def close_migrate_popup(self, get=None):
|
||||
"""用户二次确认, 关闭迁移弹窗"""
|
||||
if os.path.exists(self.migrate_task_json):
|
||||
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
|
||||
if migrate_config.get("run_status") == 2:
|
||||
migrate_config['confirm'] = 1
|
||||
public.WriteFile(self.migrate_task_json, json.dumps(migrate_config))
|
||||
return self.return_data(True, public.lang("Successfully migrated"))
|
||||
self.stop_migrate()
|
||||
return self.return_data(True, public.lang("Successfully"))
|
||||
|
||||
def stop_migrate(self, get=None):
|
||||
migrate_pid = public.ExecShell(
|
||||
"ps -ef|grep 'ssh_manager.py'|grep -v grep|awk '{print $2}'"
|
||||
)[0].replace("\n", "")
|
||||
if migrate_pid:
|
||||
public.ExecShell("kill {}".format(migrate_pid))
|
||||
public.ExecShell("rm -f /www/backup/backup_restore/migrate_backup.pl")
|
||||
public.ExecShell("rm -f /www/backup/backup_restore/migration.pl")
|
||||
public.ExecShell("rm -f /www/backup/backup_restore/migrate_backup_success.pl")
|
||||
if os.path.exists(self.migrate_task_json):
|
||||
public.ExecShell("rm -f {}".format(self.migrate_task_json))
|
||||
return self.return_data(True, public.lang("Task stopped successfully"), None, None)
|
||||
else:
|
||||
return self.return_data(error_msg=public.lang("No migration task currently"))
|
||||
|
||||
def get_migrate_progress(self, get=None):
|
||||
if os.path.exists(self.migrate_task_json):
|
||||
try:
|
||||
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
|
||||
except:
|
||||
return self.return_data(error_msg=public.lang("read migration task fail, please try again later!"))
|
||||
|
||||
migrate_config['migrate_log'] = public.ReadFile('/www/backup/backup_restore/migration.log')
|
||||
if migrate_config['run_type'] == "PANEL_INSTALL":
|
||||
migrate_config['migrate_log'] = public.ReadFile('/www/backup/backup_restore/migration.log')
|
||||
if migrate_config['run_type'] == "LOCAL_BACKUP":
|
||||
if os.path.exists('/www/backup/backup_restore/backup.log'):
|
||||
backup_log_data = public.ReadFile('/www/backup/backup_restore/backup.log')
|
||||
else:
|
||||
backup_log_data = public.lang("Starting backup task...")
|
||||
migration_log_data = public.ReadFile('/www/backup/backup_restore/migration.log')
|
||||
migrate_config['migrate_log'] = migration_log_data + "\n" + backup_log_data
|
||||
if migrate_config['run_status'] == 2:
|
||||
if migrate_config['run_type'] == "COMPLETED":
|
||||
migrate_config['migrate_progress'] = 100
|
||||
migrate_config['migrate_err_msg'] = None
|
||||
migrate_config['migrate_msg'] = public.lang("yakpanel installation completed!")
|
||||
try:
|
||||
migrate_config['panel_addr'] = migrate_config['task_info']['panel_info']['panel_url']
|
||||
migrate_config['panel_user'] = migrate_config['task_info']['panel_info']['username']
|
||||
migrate_config['panel_password'] = migrate_config['task_info']['panel_info']['password']
|
||||
except KeyError:
|
||||
return self.return_data(error_msg=public.lang(
|
||||
f"Remote panel info not found! please cancel the task and try again!"
|
||||
))
|
||||
except Exception as e:
|
||||
return self.return_data(error_msg=public.lang(f"Migration task failed! {e}"))
|
||||
else:
|
||||
migrate_config['run_status'] = 1
|
||||
|
||||
else:
|
||||
migrate_config['migrate_err_msg'] = migrate_config['migrate_msg']
|
||||
run_name = public.lang("Migration Task")
|
||||
err_info = []
|
||||
if migrate_config['run_type'] == "PANEL_INSTALL":
|
||||
run_name = public.lang("yakpanel Installation")
|
||||
elif migrate_config['run_type'] == "LOCAL_BACKUP":
|
||||
run_name = public.lang("Local Backup")
|
||||
elif migrate_config['run_type'] == "UPLOAD_FILE":
|
||||
run_name = public.lang("File Upload")
|
||||
elif migrate_config['run_type'] == "REMOTE":
|
||||
run_name = public.lang("Restore Task")
|
||||
err_info_result = {
|
||||
"name": run_name,
|
||||
"type": public.lang("Environment"),
|
||||
"msg": migrate_config['migrate_msg']
|
||||
}
|
||||
err_info.append(err_info_result)
|
||||
migrate_config['err_info'] = err_info
|
||||
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), None, migrate_config)
|
||||
else:
|
||||
return self.return_data(error_msg=public.lang("No migration task currently"))
|
||||
|
||||
def get_history_migrate_list(self, get=None):
|
||||
history_migrate = []
|
||||
if os.path.exists(self.base_path):
|
||||
for item in os.listdir(self.base_path):
|
||||
item_path = os.path.join(self.base_path, item)
|
||||
if os.path.isdir(item_path) and re.match(r'^(\d+)_migration$', item):
|
||||
timestamp = re.match(r'^(\d+)_migration$', item).group(1)
|
||||
if os.path.exists(os.path.join(item_path, "status.json")):
|
||||
status_data = json.loads(public.ReadFile(os.path.join(item_path, "status.json")))
|
||||
migrate_ip = status_data['server_ip']
|
||||
else:
|
||||
migrate_ip = None
|
||||
migrate_data = {
|
||||
"timestamp": int(timestamp),
|
||||
"migrate_time": int(timestamp),
|
||||
"migrate_path": item_path,
|
||||
"migrate_ip": migrate_ip
|
||||
}
|
||||
history_migrate.append(migrate_data)
|
||||
return history_migrate
|
||||
|
||||
def get_history_migrate_log(self, get=None):
|
||||
timestamp = get.timestamp
|
||||
history_migrate_log = self.base_path + "/" + str(timestamp) + "_migration/migration.log"
|
||||
if os.path.exists(history_migrate_log):
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), None,
|
||||
public.ReadFile(history_migrate_log))
|
||||
else:
|
||||
return self.return_data(False, public.lang("Migration log does not exist"), None, None)
|
||||
|
||||
def get_history_migrate_info(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("timestamp").Timestamp().Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
timestamp = get.timestamp
|
||||
history_migrate_info = self.base_path + "/" + str(timestamp) + "_migration/status.json"
|
||||
if os.path.exists(history_migrate_info):
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), None,
|
||||
json.loads(public.ReadFile(history_migrate_info)))
|
||||
else:
|
||||
return self.return_data(error_msg=public.lang("Migration log does not exist"))
|
||||
|
||||
def get_backup_log(self, get=None):
|
||||
if not hasattr(get, "timestamp"):
|
||||
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
|
||||
timestamp = get.timestamp
|
||||
return self.return_data(True, public.lang("Successfully retrieved"), "",
|
||||
BackupManager().get_backup_log(timestamp))
|
||||
|
||||
def ssh_net_client_check(self, server_ip, ssh_port):
|
||||
try:
|
||||
# 使用requests库测试SSH连接,设置3秒超时
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(3)
|
||||
result = sock.connect_ex((server_ip, int(ssh_port)))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
public.print_log(public.lang("SSH connection test failed: {}").format(e))
|
||||
return False
|
||||
|
||||
def del_migrate_tips(self, get=None):
|
||||
if os.path.exists("/www/server/panel/data/migration.pl"):
|
||||
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
|
||||
return public.returnMsg(True, public.lang("Migration reminder deleted successfully"))
|
||||
|
||||
def del_history_migrate(self, get=None):
|
||||
try:
|
||||
get.validate([
|
||||
Param("timestamp").Timestamp().Require(),
|
||||
], [
|
||||
public.validate.trim_filter(),
|
||||
])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
timestamp = get.timestamp
|
||||
if os.path.exists(self.base_path + "/" + str(timestamp) + "_migration"):
|
||||
public.ExecShell("rm -rf {}".format(self.base_path + "/" + str(timestamp) + "_migration"))
|
||||
return self.return_data(True, public.lang("Migration history deleted successfully"))
|
||||
else:
|
||||
return self.return_data(error_msg=public.lang("Migration history does not exist"))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名 p
|
||||
timestamp = sys.argv[2]
|
||||
com_manager = main() # 实例化对象
|
||||
if hasattr(com_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(com_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")
|
||||
112
mod/project/backup_restore/config_manager.py
Normal file
112
mod/project/backup_restore/config_manager.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <wzz@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self):
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
|
||||
def get_backup_conf(self, timestamp):
|
||||
if not os.path.exists(self.bakcup_task_json):
|
||||
return None
|
||||
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
data = [item for item in task_json_data if str(item['timestamp']) == timestamp]
|
||||
if not data:
|
||||
return None
|
||||
return data[0]
|
||||
|
||||
def save_backup_conf(self, timestamp, data):
|
||||
if not os.path.exists(self.bakcup_task_json):
|
||||
return None
|
||||
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
for item in task_json_data:
|
||||
if str(item['timestamp']) == timestamp:
|
||||
item.update(data)
|
||||
break
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(task_json_data))
|
||||
|
||||
def get_backup_data_list(self, timestamp):
|
||||
data_list_json = self.base_path + '/{timestamp}_backup/backup.json'.format(timestamp=timestamp)
|
||||
if not os.path.exists(data_list_json):
|
||||
return None
|
||||
data_list_data = json.loads(public.ReadFile(data_list_json))
|
||||
return data_list_data
|
||||
|
||||
def update_backup_data_list(self, timestamp, data_list):
|
||||
data_list_json = self.base_path + '/{timestamp}_backup/backup.json'.format(timestamp=timestamp)
|
||||
try:
|
||||
# 读取现有配置
|
||||
if os.path.exists(data_list_json):
|
||||
current_data = json.loads(public.ReadFile(data_list_json))
|
||||
# 更新数据
|
||||
current_data.update(data_list)
|
||||
data_list = current_data
|
||||
|
||||
# 写入更新后的配置
|
||||
public.WriteFile(data_list_json, json.dumps(data_list))
|
||||
return True
|
||||
except Exception as e:
|
||||
public.print_log("update_backup_data_list error: {}".format(str(e)))
|
||||
return False
|
||||
|
||||
def get_restore_data_list(self, timestamp):
|
||||
data_list_json = self.base_path + '/{timestamp}_backup/restore.json'.format(timestamp=timestamp)
|
||||
if not os.path.exists(data_list_json):
|
||||
return None
|
||||
data_list_data = json.loads(public.ReadFile(data_list_json))
|
||||
return data_list_data
|
||||
|
||||
def update_restore_data_list(self, timestamp, data_list):
|
||||
data_list_json = self.base_path + '/{timestamp}_backup/restore.json'.format(timestamp=timestamp)
|
||||
# 读取现有配置
|
||||
try:
|
||||
# 读取现有配置
|
||||
if os.path.exists(data_list_json):
|
||||
current_data = json.loads(public.ReadFile(data_list_json))
|
||||
# 更新数据
|
||||
current_data.update(data_list)
|
||||
data_list = current_data
|
||||
|
||||
# 写入更新后的配置
|
||||
public.WriteFile(data_list_json, json.dumps(data_list))
|
||||
return True
|
||||
except Exception as e:
|
||||
public.print_log("update_restore_data_list error: {}".format(str(e)))
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython config_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # IP地址
|
||||
config = ConfigManager() # 实例化对象
|
||||
if hasattr(config, method_name): # 检查方法是否存在
|
||||
method = getattr(config, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: method '{method_name}' not found")
|
||||
1189
mod/project/backup_restore/data_manager.py
Normal file
1189
mod/project/backup_restore/data_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
0
mod/project/backup_restore/modules/__init__.py
Normal file
0
mod/project/backup_restore/modules/__init__.py
Normal file
197
mod/project/backup_restore/modules/crontab_module.py
Normal file
197
mod/project/backup_restore/modules/crontab_module.py
Normal file
@@ -0,0 +1,197 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from YakPanel import app
|
||||
import crontab_v2 as crontab
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
|
||||
|
||||
class CrontabModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
|
||||
def backup_crontab_data(self, timestamp):
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Starting a Backup Scheduled Task"), "backup")
|
||||
|
||||
backup_path = self.base_path + "/{timestamp}_backup/crontab".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
field = ('id,name,type,where1,where_hour,where_minute,echo,addtime,status,'
|
||||
'save,backupTo,sName,sBody,sType,urladdress,save_local,notice,'
|
||||
'notice_channel,db_type,split_type,split_value,type_id,rname,'
|
||||
'keyword,post_param,flock,time_set,backup_mode,db_backup_path,'
|
||||
'time_type,special_time,log_cut_path,user_agent,version,table_list,result,second')
|
||||
crontab_data = public.M('crontab').order("id asc").field(field).select()
|
||||
for task in crontab_data:
|
||||
task['type_id'] = ""
|
||||
|
||||
crontab_json_path = "{}/crontab.json".format(backup_path)
|
||||
public.WriteFile(crontab_json_path, json.dumps(crontab_data))
|
||||
for item in crontab_data:
|
||||
self.print_log(public.lang("Crontab Task {} ✓".format(item['name'])), "backup")
|
||||
|
||||
public.ExecShell(f"\cp -rpa /www/server/cron/* {backup_path}/")
|
||||
crontab_info = {
|
||||
'status': 2,
|
||||
'msg': None,
|
||||
'crontab_json': crontab_json_path,
|
||||
'file_sha256': self.get_file_sha256(crontab_json_path)
|
||||
}
|
||||
self.print_log(public.lang("Backup Crontab Task completion"), 'backup')
|
||||
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
data_list['data_list']['crontab'] = crontab_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
def _add_crontab(self, crontab_item: dict, timestamp: int) -> None:
|
||||
if crontab_item['name'] in ("Domain SSL Renew Let's Encrypt Certificate", "Renew Let's Encrypt Certificate"):
|
||||
import acme_v2
|
||||
|
||||
if crontab_item['name'] == "Domain SSL Renew Let's Encrypt Certificate":
|
||||
acme_v2.acme_v2().set_crond_v2()
|
||||
elif crontab_item['name'] == "Renew Let's Encrypt Certificate":
|
||||
acme_v2.acme_v2().set_crond()
|
||||
|
||||
self.print_log(
|
||||
public.lang(f"Crontab Task: {crontab_item['name']} Add successfully ✓"),
|
||||
"restore"
|
||||
)
|
||||
return
|
||||
|
||||
if crontab_item['name'] == "[Do not delete] Resource Manager - Get Process Traffic":
|
||||
return
|
||||
|
||||
s_body = crontab_item['sBody']
|
||||
s_body = re.sub(r'sudo -u .*? bash -c \'(.*?)\'', r'\1', s_body)
|
||||
new_crontab = {
|
||||
"name": crontab_item['name'],
|
||||
"echo": crontab_item['echo'],
|
||||
"type": crontab_item['type'],
|
||||
"where1": crontab_item['where1'],
|
||||
"hour": crontab_item['where_hour'],
|
||||
"minute": crontab_item['where_minute'],
|
||||
"status": crontab_item['status'],
|
||||
"save": crontab_item['save'],
|
||||
"backupTo": crontab_item['backupTo'],
|
||||
"sType": crontab_item['sType'],
|
||||
"sBody": s_body,
|
||||
"sName": crontab_item['sName'],
|
||||
"urladdress": crontab_item['urladdress'],
|
||||
"save_local": crontab_item['save_local'],
|
||||
"notice": crontab_item['notice'],
|
||||
"notice_channel": crontab_item['notice_channel'],
|
||||
"db_type": crontab_item['db_type'],
|
||||
"split_type": crontab_item['split_type'],
|
||||
"split_value": crontab_item['split_value'],
|
||||
"keyword": crontab_item['keyword'],
|
||||
"post_param": crontab_item['post_param'],
|
||||
"flock": crontab_item['flock'],
|
||||
"time_set": crontab_item['time_set'],
|
||||
"backup_mode": crontab_item['backup_mode'],
|
||||
"db_backup_path": crontab_item['db_backup_path'],
|
||||
"time_type": crontab_item['time_type'],
|
||||
"special_time": crontab_item['special_time'],
|
||||
"user_agent": crontab_item['user_agent'],
|
||||
"version": crontab_item['version'],
|
||||
"table_list": crontab_item['table_list'],
|
||||
"result": crontab_item['result'],
|
||||
"log_cut_path": crontab_item['log_cut_path'],
|
||||
"rname": crontab_item['rname'],
|
||||
"type_id": crontab_item['type_id'],
|
||||
"second": crontab_item.get('second', ''),
|
||||
}
|
||||
result = crontab.crontab().AddCrontab(new_crontab)
|
||||
|
||||
crontab_backup_path = self.base_path + f"/{timestamp}_backup/crontab"
|
||||
|
||||
back_up_echo_file = os.path.join(crontab_backup_path, crontab_item['echo'])
|
||||
panel_echo_file = f"/www/server/cron/{crontab_item['echo']}"
|
||||
if self.overwrite or not os.path.exists(panel_echo_file):
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {back_up_echo_file} {panel_echo_file}"
|
||||
)
|
||||
|
||||
if result['status'] != 0:
|
||||
error_res = public.find_value_by_key(result, key="result", default="fail")
|
||||
self.print_log(
|
||||
public.lang(
|
||||
f"Crontab Task: {crontab_item['name']} add fail, "
|
||||
f"error: {error_res}, skip..."),
|
||||
"restore"
|
||||
)
|
||||
else:
|
||||
self.print_log(
|
||||
public.lang(f"Crontab Task: {crontab_item['name']} Add successfully ✓"),
|
||||
"restore"
|
||||
)
|
||||
|
||||
def restore_crontab_data(self, timestamp):
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Start restoring Crontab Task"), "restore")
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
cron_list = public.M('crontab').select()
|
||||
cron_name_list = [i['name'] for i in cron_list]
|
||||
crontab_data_json = restore_data['data_list']['crontab']['crontab_json']
|
||||
restore_data['data_list']['crontab']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
crontab_data = json.loads(public.ReadFile(crontab_data_json))
|
||||
with app.app_context():
|
||||
for crontab_item in crontab_data:
|
||||
if self.overwrite:
|
||||
try:
|
||||
crontab.crontab().DelCrontab(public.to_dict_obj({"id": crontab_item['id']}))
|
||||
except:
|
||||
pass
|
||||
self._add_crontab(crontab_item, timestamp)
|
||||
else: # not overwrite
|
||||
if crontab_item['name'] not in cron_name_list:
|
||||
self._add_crontab(crontab_item, timestamp)
|
||||
else:
|
||||
self.print_log(public.lang(f"Crontab Task: {crontab_item['name']} ✓"), "restore")
|
||||
self.print_log(public.lang("Crontab Task complished"), "restore")
|
||||
restore_data['data_list']['crontab']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def reload_crontab(self):
|
||||
try:
|
||||
crontab.crontab().CrondReload()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2]
|
||||
crontab_manager = CrontabModule() # 实例化对象
|
||||
if hasattr(crontab_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(crontab_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: method '{method_name}' not found")
|
||||
1052
mod/project/backup_restore/modules/database_module.py
Normal file
1052
mod/project/backup_restore/modules/database_module.py
Normal file
File diff suppressed because it is too large
Load Diff
192
mod/project/backup_restore/modules/firewall_module.py
Normal file
192
mod/project/backup_restore/modules/firewall_module.py
Normal file
@@ -0,0 +1,192 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
|
||||
from YakPanel import app
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
from firewallModelV2.comModel import main as firewall_com
|
||||
from safeModelV2.firewallModel import main as safe_firewall_main
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class FirewallModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
|
||||
def backup_firewall_data(self, timestamp):
|
||||
with app.app_context():
|
||||
try:
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Starting backup of firewall data"), "backup")
|
||||
backup_path = self.base_path + "/{timestamp}_backup/firewall".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
|
||||
port_data_path = firewall_com().export_rules(
|
||||
public.to_dict_obj({"rule": 'port', 'chain': 'ALL'})
|
||||
)['message'].get('result', '')
|
||||
ip_data_path = firewall_com().export_rules(
|
||||
public.to_dict_obj({"rule": 'ip', 'chain': 'ALL'})
|
||||
)['message'].get('result', '')
|
||||
forward_data_path = firewall_com().export_rules(
|
||||
public.to_dict_obj({"rule": 'forward'})
|
||||
)['message'].get('result', '')
|
||||
country_data_path = safe_firewall_main().export_rules(
|
||||
public.to_dict_obj({'rule_name': 'country_rule'})
|
||||
)['message'].get('result', '')
|
||||
|
||||
firewall_info = {
|
||||
"status": 2,
|
||||
"err_msg": None
|
||||
}
|
||||
|
||||
for data_path in [
|
||||
port_data_path, ip_data_path, forward_data_path, country_data_path
|
||||
]:
|
||||
if "json" in data_path:
|
||||
public.ExecShell('\cp -rpa {} {}'.format(data_path, backup_path))
|
||||
file_name = data_path.split("/")[-1]
|
||||
if "port_rule" in file_name:
|
||||
self.print_log(public.lang("Firewall port rules ✓"), 'backup')
|
||||
firewall_info["port_data_path"] = backup_path + "/" + file_name
|
||||
elif "ip_rules" in file_name:
|
||||
self.print_log(public.lang("Firewall IP rules ✓"), 'backup')
|
||||
firewall_info["ip_data_path"] = backup_path + "/" + file_name
|
||||
elif "port_forward" in file_name:
|
||||
self.print_log(public.lang("Firewall forwarding rules ✓"), 'backup')
|
||||
firewall_info["forward_data_path"] = backup_path + "/" + file_name
|
||||
elif "country" in file_name:
|
||||
self.print_log(public.lang("Firewall region rules ✓"), 'backup')
|
||||
firewall_info["country_data_path"] = backup_path + "/" + file_name
|
||||
|
||||
# 将防火墙信息写入备份配置文件
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
data_list['data_list']['firewall'] = firewall_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
except Exception as e:
|
||||
data_list['data_list']['firewall'] = {
|
||||
"status": 3,
|
||||
"err_msg": e
|
||||
}
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
self.print_log(public.lang("Firewall data backup completed"), "backup")
|
||||
|
||||
def init_firewall_data(self):
|
||||
self.print_log(public.lang("Initializing firewall data"), "restore")
|
||||
if not os.path.exists('/etc/systemd/system/BT-FirewallServices.service'):
|
||||
panel_path = public.get_panel_path()
|
||||
exec_shell = '('
|
||||
if not os.path.exists('/usr/sbin/ipset'):
|
||||
exec_shell = exec_shell + '{} install ipset -y;'.format(public.get_sys_install_bin())
|
||||
exec_shell = exec_shell + 'sh {panel_path}/script/init_firewall.sh;btpython -u {panel_path}/script/upgrade_firewall.py )'.format(
|
||||
panel_path=panel_path
|
||||
)
|
||||
public.ExecShell(exec_shell)
|
||||
return {'status': True, 'msg': public.lang('Installed.')}
|
||||
elif public.ExecShell("iptables -C INPUT -j IN_BT")[1] != '': # 丢失iptable链 需要重新创建
|
||||
exec_shell = 'sh {}/script/init_firewall.sh'.format(public.get_panel_path())
|
||||
public.ExecShell(exec_shell)
|
||||
return {'status': True, 'msg': public.lang('Installed.')}
|
||||
else:
|
||||
return {'status': True, 'msg': public.lang('Installed.')}
|
||||
|
||||
def restore_firewall_data(self, timestamp):
|
||||
with app.app_context():
|
||||
self.print_log("====================================================", "restore")
|
||||
self.print_log(public.lang("Starting restoration of firewall data"), "restore")
|
||||
self.init_firewall_data()
|
||||
resotre_data = self.get_restore_data_list(timestamp)
|
||||
firewall_data = resotre_data['data_list']['firewall']
|
||||
port_rule_file = firewall_data.get('port_data_path')
|
||||
try:
|
||||
if port_rule_file:
|
||||
if os.path.exists(port_rule_file):
|
||||
self.print_log(public.lang("Starting restoration of firewall port rules"), "restore")
|
||||
result = firewall_com().import_rules(public.to_dict_obj({"rule": 'port', 'file': port_rule_file}))
|
||||
if result['status'] == 0:
|
||||
self.print_log(public.lang("Firewall port rules restored successfully ✓"), "restore")
|
||||
else:
|
||||
self.print_log(public.lang("Failed to restore firewall port rules"), "restore")
|
||||
ip_rule_file = firewall_data.get('ip_data_path')
|
||||
if ip_rule_file:
|
||||
if os.path.exists(ip_rule_file):
|
||||
self.print_log(public.lang("Starting restoration of firewall IP rules"), "restore")
|
||||
result = firewall_com().import_rules(public.to_dict_obj({"rule": 'ip', 'file': ip_rule_file}))
|
||||
if result['status'] == 0:
|
||||
self.print_log(public.lang("Firewall IP rules restored successfully ✓"), "restore")
|
||||
else:
|
||||
self.print_log(public.lang("Failed to restore firewall IP rules"), "restore")
|
||||
|
||||
forward_rule_file = firewall_data.get('forward_data_path')
|
||||
if forward_rule_file:
|
||||
if os.path.exists(forward_rule_file):
|
||||
self.print_log(public.lang("Starting restoration of firewall forwarding rules"), "restore")
|
||||
result = firewall_com().import_rules(
|
||||
public.to_dict_obj({"rule": 'forward', 'file': forward_rule_file}))
|
||||
if result['status'] == 0:
|
||||
self.print_log(public.lang("Firewall forwarding rules restored successfully ✓"), "restore")
|
||||
else:
|
||||
self.print_log(public.lang("Failed to restore firewall forwarding rules"), "restore")
|
||||
|
||||
country_rule_file = firewall_data.get('country_data_path')
|
||||
if country_rule_file:
|
||||
if os.path.exists(country_rule_file):
|
||||
self.print_log(public.lang("Starting restoration of firewall region rules"), "restore")
|
||||
public.ExecShell('\cp -rpa {} /www/server/panel/data/firewall'.format(country_rule_file))
|
||||
country_rule_file_last_path = country_rule_file.split("/")[-1]
|
||||
result = safe_firewall_main().import_rules(
|
||||
public.to_dict_obj({'rule_name': 'country_rule', 'file_name': country_rule_file_last_path}))
|
||||
if result['status'] == 0:
|
||||
self.print_log(public.lang("Firewall region rules restored successfully ✓"), "restore")
|
||||
else:
|
||||
self.print_log(public.lang("Failed to restore firewall region rules"), "restore")
|
||||
|
||||
# 重启防火墙
|
||||
self.print_log(public.lang("Starting firewall restart"), "restore")
|
||||
firewall_com().set_status(public.to_dict_obj({'status': 1}))
|
||||
self.print_log(public.lang("Firewall restart completed"), "restore")
|
||||
resotre_data['data_list']['firewall']['status'] = 2
|
||||
self.update_restore_data_list(timestamp, resotre_data)
|
||||
except Exception as e:
|
||||
self.print_log(public.lang("Failed to restore firewall data: {}").format(str(e)), "restore")
|
||||
resotre_data['data_list']['firewall']['status'] = 3
|
||||
resotre_data['data_list']['firewall']['err_msg'] = str(e)
|
||||
self.update_restore_data_list(timestamp, resotre_data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2]
|
||||
firewall_manager = FirewallModule() # 实例化对象
|
||||
if hasattr(firewall_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(firewall_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: method '{method_name}' not found")
|
||||
138
mod/project/backup_restore/modules/ftp_module.py
Normal file
138
mod/project/backup_restore/modules/ftp_module.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from YakPanel import app
|
||||
import ftp_v2 as ftp_client
|
||||
from mod.project.backup_restore.data_manager import DataManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class FtpModule(DataManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
|
||||
def backup_ftp_data(self, timestamp=None):
|
||||
self.print_log("==================================", "backup")
|
||||
self.print_log(public.lang("Start backing up FTP account information"), "backup")
|
||||
ftp_data = public.M('ftps').field('name,pid,id,password,path,ps').select()
|
||||
filtered_ftp = []
|
||||
for ftp in ftp_data:
|
||||
try:
|
||||
if ftp.get('pid', 0) == 0:
|
||||
ftp['related_site'] = ''
|
||||
else:
|
||||
ftp['related_site'] = self._get_current_site_name_by_pid(ftp.get('pid'))
|
||||
|
||||
ftp['status'] = 2
|
||||
ftp['msg'] = None
|
||||
filtered_ftp.append(ftp)
|
||||
self.print_log(public.lang("{} user ✓").format(ftp['name']), "backup")
|
||||
except Exception as e:
|
||||
self.print_log(public.lang("Failed to backup FTP account information: {}").format(str(e)), "backup")
|
||||
ftp['status'] = 3
|
||||
ftp['msg'] = str(e)
|
||||
filtered_ftp.append(ftp)
|
||||
continue
|
||||
|
||||
self.print_log(public.lang("FTP account information backup completed"), "backup")
|
||||
return filtered_ftp
|
||||
|
||||
def _add_ftp_user(self, ftp_client: ftp_client, ftp_data: dict) -> int:
|
||||
"""
|
||||
:return: ftp_data restore_status
|
||||
"""
|
||||
log_str = public.lang("Restoring {} account").format(ftp_data['name'])
|
||||
args = public.dict_obj()
|
||||
args.ftp_username = ftp_data['name']
|
||||
args.path = ftp_data['path']
|
||||
args.ftp_password = ftp_data['password']
|
||||
args.ps = ftp_data['ps']
|
||||
args.pid = self._get_current_pid_by_site_name(
|
||||
os.path.basename(ftp_data.get('path', ''))
|
||||
)
|
||||
res = ftp_client.ftp().AddUser(args)
|
||||
if res['status'] is False:
|
||||
self.replace_log(log_str, public.lang("FTP creation failed: {}").format(res.get('message', 'create fail')),
|
||||
"restore")
|
||||
return 3
|
||||
else:
|
||||
new_log_str = public.lang("{} account ✓").format(ftp_data['name'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return 2
|
||||
|
||||
def restore_ftp_data(self, timestamp=None):
|
||||
self.print_log("====================================================", "restore")
|
||||
self.print_log(public.lang("Start restoring FTP account configuration"), "restore")
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
with app.app_context():
|
||||
for ftp_data in restore_data['data_list']['ftp']:
|
||||
try:
|
||||
if_exist = public.M('ftps').where('name=?', (ftp_data["name"],)).find()
|
||||
log_str = public.lang("Restoring {} account").format(ftp_data['name'])
|
||||
if if_exist:
|
||||
self.print_log(log_str, "restore")
|
||||
if not self.overwrite:
|
||||
self.replace_log(log_str, public.lang("{} account ✓").format(if_exist.get('name', 'ftp')),
|
||||
"restore")
|
||||
continue
|
||||
else:
|
||||
ftp_client.ftp().DeleteUser(public.to_dict_obj(
|
||||
{"id": if_exist['id'], "username": if_exist['name']}
|
||||
))
|
||||
time.sleep(0.5)
|
||||
ftp_data['restore_status'] = self._add_ftp_user(ftp_client, ftp_data)
|
||||
|
||||
else:
|
||||
ftp_data['restore_status'] = self._add_ftp_user(ftp_client, ftp_data)
|
||||
self.replace_log(
|
||||
log_str,
|
||||
public.lang("ftp: [{}] account restored successfully ✓").format(
|
||||
ftp_data.get('name', 'ftp')),
|
||||
"restore"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
self.print_log(public.lang("Failed to restore FTP account configuration: {}").format(str(e)),
|
||||
"restore")
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
self.print_log(public.lang("FTP account configuration restoration completed"), "restore")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # IP地址
|
||||
ftp_module = FtpModule() # 实例化对象
|
||||
if hasattr(ftp_module, method_name): # 检查方法是否存在
|
||||
method = getattr(ftp_module, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' not found")
|
||||
114
mod/project/backup_restore/modules/mail_module.py
Normal file
114
mod/project/backup_restore/modules/mail_module.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class MailModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.vmail_data_path = '/www/vmail'
|
||||
|
||||
def backup_vmail_data(self, timestamp):
|
||||
if not os.path.exists(self.vmail_data_path):
|
||||
return False
|
||||
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
if not data_list:
|
||||
return None
|
||||
|
||||
backup_path = self.base_path + "/{timestamp}_backup/vmail".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Start backing up mail server data"), 'backup')
|
||||
|
||||
maill_info = {
|
||||
'status': 1,
|
||||
'msg': None,
|
||||
'vmail_backup_name': None,
|
||||
}
|
||||
|
||||
data_list['data_list']['vmail'] = maill_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
vmail_backup_name = "vmail_{timestamp}.tar.gz".format(timestamp=timestamp)
|
||||
public.ExecShell("cd /www && tar -czvf {} vmail".format(vmail_backup_name))
|
||||
public.ExecShell("mv /www/{} {}".format(vmail_backup_name, backup_path))
|
||||
maill_info['vmail_backup_name'] = backup_path + "/" + vmail_backup_name
|
||||
maill_info['status'] = 2
|
||||
maill_info['msg'] = None
|
||||
maill_info['size'] = self.get_file_size(backup_path + "/" + vmail_backup_name)
|
||||
data_list['data_list']['vmail'] = maill_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
backup_size = self.format_size(self.get_file_size(backup_path + "/" + vmail_backup_name))
|
||||
self.print_log(public.lang("Mail server data backup completed. Data size: {}").format(backup_size), 'backup')
|
||||
|
||||
def restore_vmail_data(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
if not restore_data:
|
||||
return None
|
||||
|
||||
vmail_data = restore_data['data_list']['vmail']
|
||||
if not vmail_data:
|
||||
return None
|
||||
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Start restoring mail server data"), "restore")
|
||||
|
||||
restore_data['data_list']['vmail']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
vmail_backup_name = vmail_data['vmail_backup_name']
|
||||
if not os.path.exists(vmail_backup_name):
|
||||
self.print_log(public.lang("Restoration failed, file does not exist"), "restore")
|
||||
return
|
||||
|
||||
if os.path.exists(self.vmail_data_path):
|
||||
public.ExecShell("mv {} {}.bak".format(self.vmail_data_path, self.vmail_data_path))
|
||||
|
||||
public.ExecShell("\cp -rpa {} /www/{}".format(vmail_backup_name, os.path.basename(self.vmail_data_path)))
|
||||
public.ExecShell("cd /www && tar -xzvf {}".format(os.path.basename(self.vmail_data_path)))
|
||||
|
||||
restore_data['data_list']['vmail']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
self.print_log(public.lang("Mail server data restoration completed"), "restore")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # IP地址
|
||||
mail_module = MailModule() # 实例化对象
|
||||
if hasattr(mail_module, method_name): # 检查方法是否存在
|
||||
method = getattr(mail_module, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' not found")
|
||||
541
mod/project/backup_restore/modules/plugin_module.py
Normal file
541
mod/project/backup_restore/modules/plugin_module.py
Normal file
@@ -0,0 +1,541 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from YakPanel import app
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
def get_plugin_object():
|
||||
import PluginLoader
|
||||
from panel_plugin_v2 import panelPlugin
|
||||
|
||||
p = panelPlugin()
|
||||
soft_list = PluginLoader.get_plugin_list(0)
|
||||
# noinspection PyTypeChecker
|
||||
setattr(p, "_panelPlugin__plugin_s_list", panelPlugin.set_coexist(None, soft_list["list"]))
|
||||
return p
|
||||
|
||||
|
||||
class PluginModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.plugin_path = '/www/server/panel/plugin'
|
||||
self._safe_flag = False
|
||||
|
||||
def backup_plugin_data(self, timestamp):
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Start backing up plugin data"), "backup")
|
||||
|
||||
backup_path = self.base_path + "/{timestamp}_backup/plugin".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
plugin_info = {}
|
||||
btwaf_path = os.path.join(self.plugin_path, "btwaf")
|
||||
monitor_path = os.path.join(self.plugin_path, "monitor")
|
||||
tamper_core_path = os.path.join(self.plugin_path, "tamper_core")
|
||||
syssafe_path = os.path.join(self.plugin_path, "syssafe")
|
||||
|
||||
if os.path.exists(btwaf_path):
|
||||
result = self.backup_btwaf_data(timestamp)
|
||||
plugin_info['btwaf'] = result
|
||||
if os.path.exists(monitor_path):
|
||||
result = self.backup_monitor_data(timestamp)
|
||||
plugin_info['monitor'] = result
|
||||
if os.path.exists(tamper_core_path):
|
||||
result = self.backup_tamper_core_data(timestamp)
|
||||
plugin_info['tamper_core'] = result
|
||||
if os.path.exists(syssafe_path):
|
||||
result = self.backup_syssafe_data(timestamp)
|
||||
plugin_info['syssafe'] = result
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
data_list['data_list']['plugin'] = plugin_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
def backup_btwaf_data(self, timestamp):
|
||||
backup_path = self.base_path + "/{timestamp}_backup/plugin/btwaf".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
try:
|
||||
btwaf_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "btwaf", "info.json")))
|
||||
result = {
|
||||
"status": 2,
|
||||
"err_msg": None,
|
||||
"version": btwaf_info_json['versions'],
|
||||
"size": self.get_file_size(os.path.join(self.plugin_path, "btwaf"))
|
||||
}
|
||||
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/btwaf/config.json {backup_path}/config.json".format(backup_path=backup_path))
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/btwaf/site.json {backup_path}/site.json".format(backup_path=backup_path))
|
||||
public.ExecShell("\cp -rpa /www/server/btwaf/rule {backup_path}/rule".format(backup_path=backup_path))
|
||||
backup_size = self.format_size(self.get_file_size(backup_path))
|
||||
self.print_log(public.lang("Nginx firewall ✓ ({})").format(backup_size), 'backup')
|
||||
return result
|
||||
except:
|
||||
return None
|
||||
|
||||
def backup_monitor_data(self, timestamp):
|
||||
backup_path = self.base_path + "/{timestamp}_backup/plugin/monitor".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
try:
|
||||
monitor_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "monitor", "info.json")))
|
||||
result = {
|
||||
"status": 2,
|
||||
"err_msg": None,
|
||||
"version": monitor_info_json['versions'],
|
||||
"size": self.get_file_size(os.path.join(self.plugin_path, "monitor"))
|
||||
}
|
||||
if os.path.exists("/www/server/panel/plugin/monitor/site_robots.json"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/monitor/site_robots.json {backup_path}/site_robots.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/panel/plugin/monitor/site_sitemap_info.json"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/monitor/site_sitemap_info.json {backup_path}/site_sitemap_info.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/panel/plugin/monitor/spider_api.config"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/monitor/spider_api.config {backup_path}/spider_api.config".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/panel/plugin/monitor/baidu_user.config"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/monitor/baidu_user.config {backup_path}/baidu_user.config".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/panel/plugin/monitor/360_user.config"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/monitor/360_user.config {backup_path}/360_user.config".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/monitor/config {backup_path}/config".format(backup_path=backup_path))
|
||||
backup_size = self.format_size(self.get_file_size(backup_path))
|
||||
self.print_log(public.lang("Website monitoring report ✓ ({})").format(backup_size), 'backup')
|
||||
return result
|
||||
except:
|
||||
return None
|
||||
|
||||
def backup_tamper_core_data(self, timestamp):
|
||||
backup_path = self.base_path + "/{timestamp}_backup/plugin/tamper_core".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
try:
|
||||
tamper_core_info_json = json.loads(
|
||||
public.ReadFile(os.path.join(self.plugin_path, "tamper_core", "info.json")))
|
||||
result = {
|
||||
"status": 2,
|
||||
"err_msg": None,
|
||||
"version": tamper_core_info_json['versions'],
|
||||
"size": self.get_file_size(os.path.join(self.plugin_path, "tamper_core"))
|
||||
}
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/tamper_core/tamper_push_template.json {backup_path}/tamper_push_template.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/tamper_core/rule.json {backup_path}/rule.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/tamper/config_ps.json {backup_path}/config_ps.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/tamper/tamper.conf {backup_path}/tamper.conf".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
backup_size = self.format_size(self.get_file_size(backup_path))
|
||||
self.print_log(public.lang("Enterprise tamper protection ✓ ({})").format(backup_size), 'backup')
|
||||
return result
|
||||
except:
|
||||
return None
|
||||
|
||||
def backup_syssafe_data(self, timestamp):
|
||||
backup_path = self.base_path + "/{timestamp}_backup/plugin/syssafe".format(timestamp=timestamp)
|
||||
if not os.path.exists(backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(backup_path))
|
||||
|
||||
try:
|
||||
syssafe_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "syssafe", "info.json")))
|
||||
result = {
|
||||
"status": 2,
|
||||
"err_msg": None,
|
||||
"version": syssafe_info_json['versions'],
|
||||
"size": self.get_file_size(os.path.join(self.plugin_path, "syssafe"))
|
||||
}
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/syssafe/config.json {backup_path}/config.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/syssafe/sys_process.json {backup_path}/sys_process.json".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/plugin/syssafe/config {backup_path}/".format(
|
||||
backup_path=backup_path
|
||||
)
|
||||
)
|
||||
backup_size = self.format_size(self.get_file_size(backup_path))
|
||||
self.print_log(public.lang("System hardening ✓ ({})").format(backup_size), 'backup')
|
||||
return result
|
||||
except:
|
||||
return None
|
||||
|
||||
def _sys_safe_pids(self) -> list:
|
||||
try:
|
||||
cmd = """ps aux |grep -E "(bt_syssafe|syssafe_main|syssafe_pub)"|
|
||||
grep -v grep|grep -v systemctl|grep -v "init.d"|awk '{print $2}'|xargs"""
|
||||
sysafe_output = subprocess.check_output(cmd, shell=True, text=True)
|
||||
pids = sysafe_output.strip().split()
|
||||
return pids if pids else []
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def restore_plugin_data(self, timestamp):
|
||||
""""
|
||||
恢复插件数据
|
||||
"""
|
||||
self.print_log("====================================================", "restore")
|
||||
self.print_log(public.lang("Start restoring plugin data"), "restore")
|
||||
self.print_log(public.lang("If the migrated machine is not bound to a yakpanel user and upgraded to Professional version, plugin restoration failures may occur"), "restore")
|
||||
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
plugin_info = restore_data['data_list']['plugin']
|
||||
# ============================= start =====================================
|
||||
self._safe_flag = True if self._sys_safe_pids() else False
|
||||
try:
|
||||
public.ExecShell("/etc/init.d/bt_syssafe stop")
|
||||
except:
|
||||
pass
|
||||
|
||||
# ============================== btwaf =====================================
|
||||
if 'btwaf' in plugin_info and plugin_info['btwaf']:
|
||||
log_str = public.lang("Start installing Nginx firewall")
|
||||
self.print_log(log_str, "restore")
|
||||
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
plugin_version = plugin_info['btwaf']['version']
|
||||
# 检查btwaf目录下是否有正在运行的进程
|
||||
self.before_install_plugin('btwaf')
|
||||
install_result = self.install_plugin('btwaf', plugin_version)
|
||||
if install_result['status'] is True:
|
||||
new_log_str = public.lang("Nginx firewall ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
log_str = public.lang("Start restoring Nginx firewall data")
|
||||
self.print_log(log_str, "restore")
|
||||
self.restore_btwaf_data(timestamp)
|
||||
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Nginx firewall data ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 3
|
||||
restore_data['data_list']['plugin']['btwaf']['err_msg'] = install_result['msg']
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Nginx firewall ✗")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
|
||||
# ====================================== monitor ==============================================
|
||||
if 'monitor' in plugin_info and plugin_info['monitor']:
|
||||
log_str = public.lang("Start installing website monitoring report")
|
||||
self.print_log(log_str, "restore")
|
||||
restore_data['data_list']['plugin']['monitor']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
plugin_version = plugin_info['monitor']['version']
|
||||
self.before_install_plugin('monitor')
|
||||
install_result = self.install_plugin('monitor', plugin_version)
|
||||
if install_result['status'] is True:
|
||||
new_log_str = public.lang("Website monitoring report ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
log_str = public.lang("Start restoring website monitoring report data")
|
||||
self.print_log(log_str, "restore")
|
||||
self.restore_monitor_data(timestamp)
|
||||
restore_data['data_list']['plugin']['monitor']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Website monitoring report data ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
restore_data['data_list']['plugin']['monitor']['restore_status'] = 3
|
||||
restore_data['data_list']['plugin']['monitor']['err_msg'] = install_result['msg']
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Website monitoring report ✗")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
# ========================= tamper_core ======================================
|
||||
if 'tamper_core' in plugin_info and plugin_info['tamper_core']:
|
||||
log_str = public.lang("Start installing enterprise tamper protection")
|
||||
self.print_log(log_str, "restore")
|
||||
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
plugin_version = plugin_info['tamper_core']['version']
|
||||
self.before_install_plugin('tamper_core')
|
||||
install_result = self.install_plugin('tamper_core', plugin_version)
|
||||
if install_result['status'] is True:
|
||||
public.ExecShell("/etc/init.d/bt-tamper stop")
|
||||
new_log_str = public.lang("Enterprise tamper protection ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
log_str = public.lang("Start restoring enterprise tamper protection data")
|
||||
self.print_log(log_str, "restore")
|
||||
self.restore_tamper_core_data(timestamp)
|
||||
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Enterprise tamper protection data ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 3
|
||||
restore_data['data_list']['plugin']['tamper_core']['err_msg'] = install_result['msg']
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("Enterprise tamper protection ✗")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
|
||||
# ========================= syssafe ===========================================
|
||||
if 'syssafe' in plugin_info and plugin_info['syssafe']:
|
||||
log_str = public.lang("Start installing system hardening")
|
||||
self.print_log(log_str, "restore")
|
||||
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
plugin_version = plugin_info['syssafe']['version']
|
||||
self.before_install_plugin('syssafe')
|
||||
install_result = self.install_plugin('syssafe', plugin_version)
|
||||
if install_result['status'] is True:
|
||||
public.ExecShell("/etc/init.d/bt_syssafe stop")
|
||||
new_log_str = public.lang("System hardening ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
log_str = public.lang("Start restoring system hardening data")
|
||||
self.print_log(log_str, "restore")
|
||||
self.restore_syssafe_data(timestamp)
|
||||
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("System hardening data ✓")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 3
|
||||
restore_data['data_list']['plugin']['syssafe']['err_msg'] = install_result['msg']
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang("System hardening ✗")
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
|
||||
# ============================= end =====================================
|
||||
if self._safe_flag is True:
|
||||
try:
|
||||
if os.path.exists("/www/server/panel/plugin/syssafe/syssafe_main.py"):
|
||||
from plugin.syssafe.syssafe_main import syssafe_main as safe_main
|
||||
res = safe_main().set_open(None)
|
||||
public.print_log("Syssafe set_open result: {}".format(res))
|
||||
except Exception as e:
|
||||
public.print_log("Failed to start syssafe: {}".format(str(e)))
|
||||
|
||||
self.print_log(public.lang("Plugin data restoration complete"), "restore")
|
||||
|
||||
def restore_btwaf_data(self, timestamp):
|
||||
restore_path = self.base_path + "/{timestamp}_backup/plugin/btwaf".format(timestamp=timestamp)
|
||||
plugin_path = "/www/server/btwaf"
|
||||
if os.path.exists(restore_path) and os.path.exists(plugin_path):
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/config.json /www/server/btwaf/config.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/site.json /www/server/btwaf/site.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/rule/* /www/server/btwaf/rule".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
|
||||
def restore_monitor_data(self, timestamp):
|
||||
restore_path = self.base_path + "/{timestamp}_backup/plugin/monitor".format(timestamp=timestamp)
|
||||
plugin_path = "/www/server/panel/plugin/monitor/"
|
||||
if os.path.exists(restore_path) and os.path.exists(plugin_path):
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/site_robots.json /www/server/panel/plugin/monitor/site_robots.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/site_sitemap_info.json /www/server/panel/plugin/monitor/site_sitemap_info.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/spider_api.config /www/server/panel/plugin/monitor/spider_api.config".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/baidu_user.config /www/server/panel/plugin/monitor/baidu_user.config".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/360_user.config /www/server/panel/plugin/monitor/360_user.config".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/config/* /www/server/monitor/config".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
|
||||
def restore_tamper_core_data(self, timestamp):
|
||||
restore_path = self.base_path + "/{timestamp}_backup/plugin/tamper_core".format(timestamp=timestamp)
|
||||
plugin_path = "/www/server/panel/plugin/tamper_core/"
|
||||
if os.path.exists(restore_path) and os.path.exists(plugin_path):
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/tamper_push_template.json /www/server/panel/plugin/tamper_core/tamper_push_template.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/rule.json /www/server/panel/plugin/tamper_core/rule.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/config_ps.json /www/server/tamper/config_ps.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/tamper.conf /www/server/tamper/tamper.conf".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell("/etc/init.d/bt-tamper stop")
|
||||
|
||||
def restore_syssafe_data(self, timestamp):
|
||||
restore_path = self.base_path + "/{timestamp}_backup/plugin/syssafe".format(timestamp=timestamp)
|
||||
plugin_path = "/www/server/panel/plugin/syssafe/"
|
||||
if os.path.exists(restore_path) and os.path.exists(plugin_path):
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/config.json /www/server/panel/plugin/syssafe/config.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/sys_process.json /www/server/panel/plugin/syssafe/sys_process.json".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"\cp -rpa {restore_path}/config/* /www/server/panel/plugin/syssafe/config".format(
|
||||
restore_path=restore_path
|
||||
)
|
||||
)
|
||||
public.ExecShell("/etc/init.d/bt_syssafe stop")
|
||||
|
||||
def before_install_plugin(self, plugin_name: str):
|
||||
try:
|
||||
if plugin_name == "btwaf":
|
||||
cmd = "ps aux | grep 'BT-WAF' | grep -v grep | awk '{print $2}'"
|
||||
cmd_output = subprocess.check_output(cmd, shell=True, text=True)
|
||||
pids1 = cmd_output.strip()
|
||||
if pids1:
|
||||
subprocess.run(["kill", "-9", str(pids1)], check=True)
|
||||
xss_path = "/www/server/panel/plugin/btwaf/nginx_btwaf_xss"
|
||||
lsof_output = subprocess.check_output(f"lsof -t {xss_path}", shell=True, text=True)
|
||||
pids2 = lsof_output.strip().split()
|
||||
for p2 in pids2:
|
||||
subprocess.run(["kill", "-9", str(p2)], check=True)
|
||||
time.sleep(1)
|
||||
elif plugin_name == "syssafe":
|
||||
public.ExecShell("/etc/init.d/bt_syssafe stop")
|
||||
time.sleep(1)
|
||||
elif plugin_name == "monitor":
|
||||
pass
|
||||
elif plugin_name == "tamper_core":
|
||||
pass
|
||||
time.sleep(1)
|
||||
except:
|
||||
pass
|
||||
|
||||
def install_plugin(self, sName, plugin_version):
|
||||
with app.app_context():
|
||||
try:
|
||||
plugin = get_plugin_object()
|
||||
version_parts = plugin_version.split(".", 1)
|
||||
sVersion = version_parts[0]
|
||||
sMin_version = version_parts[1] if len(version_parts) > 1 else ""
|
||||
get = public.dict_obj()
|
||||
get.sName = sName
|
||||
get.version = sVersion
|
||||
get.min_version = sMin_version
|
||||
info = plugin.install_plugin(get)["message"]
|
||||
args = public.dict_obj()
|
||||
args.tmp_path = info.get("tmp_path")
|
||||
args.plugin_name = sName
|
||||
args.install_opt = info.get("install_opt")
|
||||
info = plugin.input_package(args)
|
||||
return info
|
||||
except Exception:
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
return {
|
||||
'status': False,
|
||||
'msg': public.lang('Installation failed')
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2]
|
||||
plugin_manager = PluginModule() # 实例化对象
|
||||
if hasattr(plugin_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(plugin_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' not found")
|
||||
911
mod/project/backup_restore/modules/site_module.py
Normal file
911
mod/project/backup_restore/modules/site_module.py
Normal file
@@ -0,0 +1,911 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from public.hook_import import hook_import
|
||||
|
||||
hook_import()
|
||||
from wp_toolkit import wpbackup
|
||||
import db
|
||||
from YakPanel import app
|
||||
import panel_site_v2 as panelSite
|
||||
from mod.project.backup_restore.data_manager import DataManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class SiteModule(DataManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.site_dir_auth_path = "/www/server/panel/data/site_dir_auth.json"
|
||||
self.redirect_conf_path = "/www/server/panel/data/redirect.conf"
|
||||
self.proxy_conf_path = "/www/server/panel/data/proxyfile.json"
|
||||
|
||||
@staticmethod
|
||||
def copy_directory(src: str, dst: str, overwrite: bool = False) -> None:
|
||||
"""
|
||||
site 复制文件夹
|
||||
src: 源路径
|
||||
dst: 目标路径
|
||||
overwrite: 是否覆盖
|
||||
"""
|
||||
if not src or not dst:
|
||||
return
|
||||
if src == dst:
|
||||
return
|
||||
if isinstance(overwrite, int):
|
||||
overwrite = False if overwrite == 0 else True
|
||||
|
||||
def _copy2(src_file: str, dst_file: str):
|
||||
if overwrite or not os.path.exists(dst_file):
|
||||
try:
|
||||
shutil.copy2(src_file, dst_file)
|
||||
except:
|
||||
try:
|
||||
public.ExecShell("chattr -i {}".format(dst_file))
|
||||
shutil.copy2(src_file, dst_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 如果源路径不存在,直接返回
|
||||
if not os.path.exists(src):
|
||||
return
|
||||
|
||||
# 确保目标目录存在
|
||||
if not os.path.exists(dst):
|
||||
os.makedirs(dst, 0o755, exist_ok=True)
|
||||
|
||||
# 复制源目录下的所有内容到目标目录
|
||||
for item in os.listdir(src):
|
||||
src_item = os.path.join(src, item)
|
||||
dst_item = os.path.join(dst, item)
|
||||
|
||||
if os.path.isdir(src_item):
|
||||
# 递归调用自身来复制子目录内容
|
||||
SiteModule.copy_directory(src_item, dst_item, overwrite)
|
||||
else:
|
||||
# 复制文件
|
||||
_copy2(src_item, dst_item)
|
||||
|
||||
@staticmethod
|
||||
def _db_cp(source_db: str, target_db: str, tables: list = None) -> None:
|
||||
"""
|
||||
指定表复制到新db中
|
||||
source_db: 源db路径
|
||||
target_db: 目标db路径
|
||||
tables: 要复制的表列表, None时复制所有
|
||||
"""
|
||||
if not source_db:
|
||||
return
|
||||
source_db = f"'{source_db}'"
|
||||
target_db = f"'{target_db}'"
|
||||
if not tables:
|
||||
public.ExecShell(f"sqlite3 {source_db} .dump | sqlite3 {target_db}")
|
||||
return
|
||||
|
||||
for table in tables:
|
||||
public.ExecShell(f"sqlite3 {target_db} 'DROP TABLE IF EXISTS {table};'")
|
||||
|
||||
tables_for_dump = " ".join(tables)
|
||||
tables_cmd = f"sqlite3 {source_db} '.dump {tables_for_dump}'"
|
||||
|
||||
tables_for_sql_in = ", ".join([f"'{t}'" for t in tables])
|
||||
triggers_cmd = f"""sqlite3 {source_db} \"
|
||||
SELECT sql || ';' FROM sqlite_master WHERE type='trigger' AND tbl_name IN ({tables_for_sql_in}) AND sql IS NOT NULL;\"
|
||||
"""
|
||||
res_cmd = f"({tables_cmd}; {triggers_cmd}) | sqlite3 {target_db}"
|
||||
public.ExecShell(res_cmd)
|
||||
|
||||
public.ExecShell(f"sqlite3 {target_db} '.dump' | sqlite3 {target_db}")
|
||||
|
||||
@staticmethod
|
||||
def chmod_dir_file(path: str, dir_mode: int = 0o755, file_mode: int = 0o644):
|
||||
if not path:
|
||||
return
|
||||
for root, dirs, files in os.walk(path):
|
||||
for d in dirs:
|
||||
try:
|
||||
os.chmod(os.path.join(root, d), dir_mode)
|
||||
except:
|
||||
continue
|
||||
for f in files:
|
||||
try:
|
||||
os.chmod(os.path.join(root, f), file_mode)
|
||||
except:
|
||||
continue
|
||||
if os.path.isdir(path):
|
||||
try:
|
||||
os.chmod(path, dir_mode)
|
||||
except:
|
||||
pass
|
||||
elif os.path.isfile(path):
|
||||
try:
|
||||
os.chmod(path, file_mode)
|
||||
except:
|
||||
pass
|
||||
|
||||
def get_site_backup_conf(self, timestamp=None):
|
||||
# todo node, 待优化
|
||||
site_data = public.M('sites').where("project_type != ?", "Node").field('name,path,project_type,id,ps').select()
|
||||
domian_data = public.M('domain').field('name,id,pid,id,port').select()
|
||||
wp_onekey = public.M('wordpress_onekey').field('s_id,prefix,user,pass').select()
|
||||
|
||||
filtered_sites = [site for site in site_data]
|
||||
filtered_domain = [name for name in domian_data]
|
||||
|
||||
pid_map = {}
|
||||
for domain in filtered_domain:
|
||||
pid = domain["pid"]
|
||||
if pid not in pid_map:
|
||||
pid_map[pid] = []
|
||||
|
||||
pid_map[pid].append(
|
||||
{
|
||||
"name": domain["name"],
|
||||
"port": domain["port"],
|
||||
}
|
||||
)
|
||||
|
||||
for site in filtered_sites:
|
||||
# domain
|
||||
site_id = site["id"]
|
||||
if site_id in pid_map:
|
||||
site["domains"] = pid_map[site_id]
|
||||
|
||||
# wp prefix
|
||||
hit = False
|
||||
for p in wp_onekey:
|
||||
try: # wp may be not exist
|
||||
if p["s_id"] == site["id"] and p.get('prefix'):
|
||||
site["wp_onekey"] = {
|
||||
"prefix": p['prefix'],
|
||||
"user": p.get('user', ''),
|
||||
"pass": p.get('pass', ''),
|
||||
}
|
||||
hit = True
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if not hit:
|
||||
site["wp_onekey"] = {}
|
||||
|
||||
site["data_type"] = "backup"
|
||||
site["status"] = 0
|
||||
site["msg"] = None
|
||||
|
||||
return filtered_sites
|
||||
|
||||
def backup_site_data(self, timestamp):
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
if not data_list:
|
||||
return None
|
||||
data_backup_path = data_list['backup_path']
|
||||
site_backup_path = data_backup_path + '/site/'
|
||||
if not os.path.exists(site_backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(site_backup_path))
|
||||
self.print_log("====================================================", 'backup')
|
||||
self.print_log(public.lang("Start backing up site data"), 'backup')
|
||||
|
||||
self.backup_site_config(site_backup_path)
|
||||
|
||||
site_sql = db.Sql()
|
||||
site_sql.table('sites')
|
||||
domain_sql = db.Sql()
|
||||
domain_sql.table('domain')
|
||||
|
||||
for site in data_list['data_list']['site']:
|
||||
# 备份db数据库数据
|
||||
site_id = site['id']
|
||||
site_db_record = site_sql.where('id=?', (site_id,)).find()
|
||||
site['site_db_record'] = site_db_record
|
||||
|
||||
# 备份网站数据
|
||||
site['path'] = str(site['path']).rstrip('/')
|
||||
last_path = os.path.basename(site['path'])
|
||||
site["last_path"] = last_path
|
||||
site_path = site_backup_path + last_path
|
||||
|
||||
if site["project_type"] == "PHP":
|
||||
try:
|
||||
site["php_ver"] = panelSite.panelSite().GetSitePHPVersion(
|
||||
public.to_dict_obj({'siteName': site['name']})
|
||||
)['phpversion']
|
||||
except:
|
||||
site["php_ver"] = None
|
||||
|
||||
site['status'] = 1
|
||||
log_str = public.lang("Backing up {} project: {}").format(site['project_type'], site['name'])
|
||||
self.print_log(log_str, "backup")
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
# 备份网站项目
|
||||
public.ExecShell("cp -rpa {} {}".format(site['path'], site_path))
|
||||
site_zip = site_backup_path + last_path + ".zip"
|
||||
public.ExecShell("cd {} && zip -r {}.zip {}".format(site_backup_path, last_path, last_path))
|
||||
if os.path.exists(site_zip):
|
||||
site_zip_size = public.ExecShell("du -sb {}".format(site_zip))[0].split("\t")[0]
|
||||
site['data_file_name'] = site_zip
|
||||
site['size'] = site_zip_size
|
||||
site['zip_sha256'] = self.get_file_sha256(site_zip)
|
||||
|
||||
# 创建配置文件备份目录
|
||||
webserver_conf_path = ["apache", "cert", "config", "nginx", "open_basedir",
|
||||
"openlitespeed", "other_php", "rewrite", "ssl",
|
||||
"ssl_saved", "template", "tomcat"]
|
||||
conf_backup_path = site_backup_path + site['name'] + "_conf/"
|
||||
public.ExecShell(f"mkdir -p '{conf_backup_path}'")
|
||||
|
||||
# 创建子目录
|
||||
for wpath in webserver_conf_path:
|
||||
web_conf_backup_path = conf_backup_path + wpath
|
||||
public.ExecShell(f"mkdir -p '{web_conf_backup_path}'")
|
||||
|
||||
# 备份网站配置文件
|
||||
self.backup_web_conf(site['name'], conf_backup_path)
|
||||
|
||||
# 打包网站配置文件
|
||||
site_name = site['name']
|
||||
site_conf_zip = site_backup_path + site_name + "_conf.zip"
|
||||
public.ExecShell("cd {} && zip -r {}_conf.zip {}_conf".format(site_backup_path, site_name, site_name))
|
||||
if os.path.exists(site_conf_zip):
|
||||
site['conf_file_name'] = site_conf_zip
|
||||
site['zip_sha256'] = self.get_file_sha256(site_conf_zip)
|
||||
site['conf_sha256'] = self.get_file_sha256(site_conf_zip)
|
||||
|
||||
site['status'] = 2
|
||||
format_backup_file_size = self.format_size(int(site['size']))
|
||||
new_log_str = public.lang("{} project {} ✓ ({})").format(
|
||||
site['project_type'], site['name'], format_backup_file_size
|
||||
)
|
||||
self.replace_log(log_str, new_log_str, 'backup')
|
||||
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
self.print_log(public.lang("Site data backup completed"), 'backup')
|
||||
|
||||
def backup_site_config(self, site_backup_path):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/data/default.db {site_backup_path}default.db".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
# 备份加密访问配置文件
|
||||
if os.path.exists("/www/server/panel/data/site_dir_auth.json"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/data/site_dir_auth.json {site_backup_path}site_dir_auth.json".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
# 备份加密密码
|
||||
if os.path.exists("/www/server/pass/"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/pass/ {site_backup_path}pass/".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
# 备份反代配置
|
||||
if os.path.exists("/www/server/proxy_project/sites"):
|
||||
public.ExecShell("mkdir -p {site_backup_path}proxy_project/".format(site_backup_path=site_backup_path))
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/proxy_project/sites {site_backup_path}proxy_project/sites/".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
# 备份重定向配置
|
||||
if os.path.exists("/www/server/panel/data/redirect.conf"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/data/redirect.conf {site_backup_path}redirect.conf".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
if os.path.exists("/www/server/panel/data/proxyfile.json"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/data/proxyfile.json {site_backup_path}proxyfile.json".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
# 备份wp加速配置文件
|
||||
if os.path.exists("/www/server/nginx/conf/"):
|
||||
nginx_conf_list = os.listdir("/www/server/nginx/conf/")
|
||||
for nginx_conf_name in nginx_conf_list:
|
||||
if "wpfastcgi" in nginx_conf_name:
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/nginx/conf/{nginx_conf_name} {site_backup_path}{nginx_conf_name}".format(
|
||||
nginx_conf_name=nginx_conf_name, site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
# 备份well-known文件
|
||||
if os.path.exists("/www/server/panel/vhost/nginx/well-known"):
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/vhost/nginx/well-known {site_backup_path}/well-known".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
public.ExecShell("mkdir -p {site_backup_path}/monitor_conf/".format(site_backup_path=site_backup_path))
|
||||
public.ExecShell(
|
||||
"\cp -rpa /www/server/panel/vhost/nginx/0.monitor*.conf {site_backup_path}/monitor_conf/".format(
|
||||
site_backup_path=site_backup_path
|
||||
)
|
||||
)
|
||||
|
||||
def restore_site_config(self, backup_path):
|
||||
default_db_file = backup_path + "default.db"
|
||||
dir_auth_file = backup_path + "site_dir_auth.json"
|
||||
pass_path = backup_path + "pass"
|
||||
proxy_project_path = backup_path + "proxy_project"
|
||||
redirect_file = backup_path + "redirect.conf"
|
||||
proxyfile_file = backup_path + "proxyfile.json"
|
||||
if os.path.exists(default_db_file) and self.overwrite:
|
||||
panel_current = public.S("users").find()
|
||||
public.ExecShell(f"\cp -rpa {default_db_file} /www/server/panel/data/default.db")
|
||||
os.chmod("/www/server/panel/data/default.db", 0o600)
|
||||
if "id" in panel_current:
|
||||
del panel_current["id"]
|
||||
public.S("users").where("id=?", (1,)).update(panel_current)
|
||||
if os.path.exists(pass_path):
|
||||
self.copy_directory(
|
||||
src=pass_path,
|
||||
dst="/www/server/pass",
|
||||
overwrite=self.overwrite,
|
||||
)
|
||||
self.chmod_dir_file("/www/server/pass", file_mode=0o644)
|
||||
|
||||
if os.path.exists(proxy_project_path):
|
||||
target = "/www/server/proxy_project"
|
||||
self.copy_directory(
|
||||
src=proxy_project_path,
|
||||
dst=target,
|
||||
overwrite=self.overwrite,
|
||||
)
|
||||
self.chmod_dir_file(target, file_mode=0o644)
|
||||
|
||||
if os.path.exists(dir_auth_file):
|
||||
target = "/www/server/panel/data/site_dir_auth.json"
|
||||
if not os.path.exists(target) or self.overwrite:
|
||||
public.ExecShell(f"\cp -rpa {dir_auth_file} /www/server/panel/data/site_dir_auth.json")
|
||||
self.chmod_dir_file(target, file_mode=0o600)
|
||||
|
||||
if os.path.exists(redirect_file):
|
||||
target = "/www/server/panel/data/redirect.conf"
|
||||
if not os.path.exists(target) or self.overwrite:
|
||||
public.ExecShell(f"\cp -rpa {redirect_file} /www/server/panel/data/redirect.conf")
|
||||
self.chmod_dir_file(target, file_mode=0o600)
|
||||
|
||||
if os.path.exists(proxyfile_file):
|
||||
target = "/www/server/panel/data/proxyfile.json"
|
||||
if not os.path.exists(target) or self.overwrite:
|
||||
public.ExecShell(f"\cp -rpa {proxyfile_file} /www/server/panel/data/proxyfile.json")
|
||||
self.chmod_dir_file(target, file_mode=0o600)
|
||||
|
||||
public.ExecShell(f"\cp -rpa {backup_path}/*wpfastcgi.conf /www/server/nginx/conf/")
|
||||
self.chmod_dir_file("/www/server/nginx/conf", file_mode=0o644)
|
||||
|
||||
if os.path.exists(backup_path + "well-known"):
|
||||
target = "/www/server/panel/vhost/nginx/well-known"
|
||||
if not os.path.exists(target):
|
||||
public.ExecShell(f"mkdir -p {target}")
|
||||
public.ExecShell(f"\cp -rpa {backup_path}well-known/* /www/server/panel/vhost/nginx/well-known/")
|
||||
self.chmod_dir_file(target, dir_mode=0o600, file_mode=0o600)
|
||||
|
||||
public.ExecShell(f"\cp -rpa {backup_path}monitor_conf/* /www/server/panel/vhost/nginx/")
|
||||
self.chmod_dir_file("/www/server/panel/vhost/nginx", dir_mode=0o600, file_mode=0o600)
|
||||
|
||||
def restore_site_python_env(self, timestamp):
|
||||
self.print_log("================================================", "restore")
|
||||
self.print_log(public.lang("Starting to restore site Python dependencies..."), 'restore')
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
site_data = restore_data['data_list']['site']
|
||||
for site in site_data:
|
||||
if site['project_type'] == 'Python':
|
||||
python_site_config = site['site_db_record']['project_config']
|
||||
requirement_path = json.loads(python_site_config)['requirement_path']
|
||||
vpath = json.loads(python_site_config)['vpath']
|
||||
if requirement_path:
|
||||
pip3_path = vpath + "/bin/pip3"
|
||||
pip2_path = vpath + "/bin/pip2"
|
||||
pip_install_cmd = None
|
||||
if os.path.exists(pip3_path):
|
||||
pip_install_cmd = "{} install -r {}".format(pip3_path, requirement_path)
|
||||
elif os.path.exists(pip2_path):
|
||||
pip_install_cmd = "{} install -r {}".format(pip2_path, requirement_path)
|
||||
|
||||
if pip_install_cmd:
|
||||
public.ExecShell(pip_install_cmd)
|
||||
self.print_log(public.lang("Site Python dependencies restoration completed"), 'restore')
|
||||
|
||||
def backup_web_conf(self, site_name: str, conf_backup_path: str) -> None:
|
||||
"""备份网站配置文件
|
||||
|
||||
Args:
|
||||
site_name: 网站名称
|
||||
conf_backup_path: 配置文件备份路径
|
||||
"""
|
||||
# 定义需要备份的配置文件和路径映射
|
||||
conf_paths = {
|
||||
'cert': "/www/server/panel/vhost/cert/{site_name}".format(site_name=site_name),
|
||||
'rewrite': "/www/server/panel/vhost/rewrite/{site_name}.conf".format(site_name=site_name),
|
||||
'nginx': {
|
||||
'main': "/www/server/panel/vhost/nginx/{site_name}.conf".format(site_name=site_name),
|
||||
'redirect': "/www/server/panel/vhost/nginx/redirect/{site_name}".format(site_name=site_name),
|
||||
'proxy': "/www/server/panel/vhost/nginx/proxy/{site_name}".format(site_name=site_name),
|
||||
'dir_auth': "/www/server/panel/vhost/nginx/dir_auth/{site_name}".format(site_name=site_name)
|
||||
},
|
||||
'apache': {
|
||||
'main': "/www/server/panel/vhost/apache/{site_name}.conf".format(site_name=site_name),
|
||||
'redirect': "/www/server/panel/vhost/apache/redirect/{site_name}".format(site_name=site_name),
|
||||
'proxy': "/www/server/panel/vhost/apache/proxy/{site_name}".format(site_name=site_name),
|
||||
'dir_auth': "/www/server/panel/vhost/apache/dir_auth/{site_name}".format(site_name=site_name)
|
||||
},
|
||||
|
||||
'openlitespeed': {
|
||||
'main': '/www/server/panel/vhost/openlitespeed',
|
||||
'detail': '/www/server/panel/vhost/openlitespeed/detail',
|
||||
'listen': '/www/server/panel/vhost/openlitespeed/listen',
|
||||
'ssl': '/www/server/panel/vhost/openlitespeed/detail/ssl',
|
||||
},
|
||||
}
|
||||
|
||||
# 备份证书
|
||||
if os.path.exists(conf_paths['cert']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}cert/")
|
||||
public.ExecShell(f"\cp -rpa {conf_paths['cert']} {conf_backup_path}cert/")
|
||||
|
||||
# 备份伪静态
|
||||
if os.path.exists(conf_paths['rewrite']):
|
||||
public.ExecShell(f"\cp -rpa {conf_paths['rewrite']} {conf_backup_path}rewrite")
|
||||
|
||||
rewrite_file_list = os.listdir("/www/server/panel/vhost/rewrite/")
|
||||
for rewrite_file in rewrite_file_list:
|
||||
if rewrite_file.endswith(".conf"):
|
||||
if site_name in rewrite_file:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa /www/server/panel/vhost/rewrite/{rewrite_file} {conf_backup_path}rewrite"
|
||||
)
|
||||
|
||||
# 备份nginx配置
|
||||
nginx_paths = conf_paths['nginx']
|
||||
if os.path.exists(nginx_paths['main']):
|
||||
public.ExecShell(f"\cp -rpa {nginx_paths['main']} {conf_backup_path}nginx/")
|
||||
|
||||
if not os.path.exists(nginx_paths['main']):
|
||||
web_conf_list = os.listdir("/www/server/panel/vhost/nginx/")
|
||||
for web_conf_name in web_conf_list:
|
||||
if web_conf_name.endswith(".conf"):
|
||||
if site_name in web_conf_name:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa /www/server/panel/vhost/nginx/{web_conf_name} {conf_backup_path}nginx/"
|
||||
)
|
||||
|
||||
if os.path.exists(nginx_paths['redirect']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/redirect/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {nginx_paths['redirect']}/* {conf_backup_path}nginx/redirect/{site_name}/")
|
||||
|
||||
if os.path.exists(nginx_paths['proxy']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/proxy/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {nginx_paths['proxy']}/* {conf_backup_path}nginx/proxy/{site_name}/")
|
||||
|
||||
if os.path.exists(nginx_paths['dir_auth']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/dir_auth/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {nginx_paths['dir_auth']}/* {conf_backup_path}nginx/dir_auth/{site_name}/")
|
||||
|
||||
# 备份apache配置
|
||||
apache_paths = conf_paths['apache']
|
||||
if os.path.exists(apache_paths['main']):
|
||||
public.ExecShell(f"\cp -rpa {apache_paths['main']} {conf_backup_path}apache/")
|
||||
|
||||
if not os.path.exists(apache_paths['main']):
|
||||
web_conf_list = os.listdir("/www/server/panel/vhost/apache/")
|
||||
for web_conf_name in web_conf_list:
|
||||
if web_conf_name.endswith(".conf"):
|
||||
if site_name in web_conf_name:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa /www/server/panel/vhost/apache/{web_conf_name} {conf_backup_path}apache/"
|
||||
)
|
||||
|
||||
if os.path.exists(apache_paths['redirect']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}apache/redirect/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {apache_paths['redirect']}/* {conf_backup_path}apache/redirect/{site_name}/")
|
||||
|
||||
if os.path.exists(apache_paths['proxy']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}apache/proxy/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {apache_paths['proxy']}/* {conf_backup_path}apache/proxy/{site_name}/")
|
||||
|
||||
if os.path.exists(apache_paths['dir_auth']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}apache/dir_auth/{site_name}/")
|
||||
public.ExecShell(f"\cp -rpa {apache_paths['dir_auth']}/* {conf_backup_path}apache/dir_auth/{site_name}/")
|
||||
|
||||
# 备份openlitespeed配置
|
||||
ols_paths = conf_paths['openlitespeed']
|
||||
if os.path.exists(ols_paths['main']):
|
||||
for web_conf_name in os.listdir(ols_paths['main']):
|
||||
if site_name in web_conf_name:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa /www/server/panel/vhost/openlitespeed/{web_conf_name} {conf_backup_path}openlitespeed/"
|
||||
)
|
||||
|
||||
if os.path.exists(conf_paths['openlitespeed']['detail']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}openlitespeed/detail")
|
||||
for detail in os.listdir(conf_paths['openlitespeed']['detail']):
|
||||
if site_name in detail:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {ols_paths['main']}/detail/{detail} {conf_backup_path}openlitespeed/detail/"
|
||||
)
|
||||
|
||||
if os.path.exists(conf_paths['openlitespeed']['listen']):
|
||||
public.ExecShell(
|
||||
f"cp -rpa {conf_paths['openlitespeed']['listen']} {conf_backup_path}openlitespeed/listen"
|
||||
)
|
||||
|
||||
if os.path.exists(conf_paths['openlitespeed']['ssl']):
|
||||
public.ExecShell(f"mkdir -p {conf_backup_path}openlitespeed/detail/ssl")
|
||||
for ssl in os.listdir(conf_paths['openlitespeed']['ssl']):
|
||||
if site_name in ssl:
|
||||
public.ExecShell(
|
||||
f"cp -rpa {conf_paths['openlitespeed']['ssl']}/{ssl} {conf_backup_path}openlitespeed/detail/ssl/{ssl}"
|
||||
)
|
||||
|
||||
def restore_web_conf(self, site_name: str, conf_backup_path: str) -> None:
|
||||
"""还原网站配置文件
|
||||
|
||||
Args:
|
||||
site_name: 网站名称
|
||||
conf_backup_path: 配置文件备份路径
|
||||
"""
|
||||
# 定义需要还原的配置文件和路径映射
|
||||
conf_paths = {
|
||||
'cert': "/www/server/panel/vhost/cert/{site_name}".format(site_name=site_name),
|
||||
'rewrite': "/www/server/panel/vhost/rewrite/{site_name}.conf".format(site_name=site_name),
|
||||
'nginx': {
|
||||
'main': "/www/server/panel/vhost/nginx/{site_name}.conf".format(site_name=site_name),
|
||||
'redirect': "/www/server/panel/vhost/nginx/redirect/{site_name}".format(site_name=site_name),
|
||||
'proxy': "/www/server/panel/vhost/nginx/proxy/{site_name}".format(site_name=site_name)
|
||||
},
|
||||
'apache': {
|
||||
'main': "/www/server/panel/vhost/apache/{site_name}.conf".format(site_name=site_name),
|
||||
'redirect': "/www/server/panel/vhost/apache/redirect/{site_name}".format(site_name=site_name),
|
||||
'proxy': "/www/server/panel/vhost/apache/proxy/{site_name}".format(site_name=site_name)
|
||||
},
|
||||
'openlitespeed': {
|
||||
'main': '/www/server/panel/vhost/openlitespeed',
|
||||
'detail': '/www/server/panel/vhost/openlitespeed/detail',
|
||||
'listen': '/www/server/panel/vhost/openlitespeed/listen',
|
||||
'ssl': '/www/server/panel/vhost/openlitespeed/detail/ssl',
|
||||
},
|
||||
}
|
||||
|
||||
# 还原证书
|
||||
if os.path.exists(f"{conf_backup_path}cert"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}cert {conf_paths['cert']}")
|
||||
|
||||
# 还原伪静态
|
||||
if os.path.exists(f"{conf_backup_path}rewrite"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}rewrite {conf_paths['rewrite']}")
|
||||
|
||||
# 还原nginx配置
|
||||
if os.path.exists(f"{conf_backup_path}nginx"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx {conf_paths['nginx']['main']}")
|
||||
if os.path.exists(f"{conf_backup_path}nginx/redirect"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx/redirect {conf_paths['nginx']['redirect']}")
|
||||
if os.path.exists(f"{conf_backup_path}nginx/proxy"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx/proxy {conf_paths['nginx']['proxy']}")
|
||||
|
||||
# 还原apache配置
|
||||
if os.path.exists(f"{conf_backup_path}apache"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}apache {conf_paths['apache']['main']}")
|
||||
if os.path.exists(f"{conf_backup_path}apache/redirect"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}apache/redirect {conf_paths['apache']['redirect']}")
|
||||
if os.path.exists(f"{conf_backup_path}apache/proxy"):
|
||||
public.ExecShell(f"\cp -rpa {conf_backup_path}apache/proxy {conf_paths['apache']['proxy']}")
|
||||
|
||||
# 还原openlitespeed配置
|
||||
if os.path.exists(f"{conf_backup_path}openlitespeed"):
|
||||
for web_cf_name in os.listdir(f"{conf_backup_path}openlitespeed"):
|
||||
if site_name in web_cf_name:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {conf_backup_path}openlitespeed/{web_cf_name} {conf_paths['openlitespeed']['main']}/{web_cf_name}"
|
||||
)
|
||||
|
||||
detail_path = f"{conf_backup_path}openlitespeed/detail"
|
||||
if os.path.exists(detail_path):
|
||||
if not os.path.exists(conf_paths['openlitespeed']['detail']):
|
||||
public.ExecShell(f"mkdir -p {conf_paths['openlitespeed']['detail']}")
|
||||
for detail in os.listdir(detail_path):
|
||||
if site_name in detail:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {detail_path}/{detail} {conf_paths['openlitespeed']['detail']}/{detail}"
|
||||
)
|
||||
|
||||
listen_path = f"{conf_backup_path}openlitespeed/listen"
|
||||
if os.path.exists(listen_path):
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {listen_path} {conf_paths['openlitespeed']['listen']}/listen"
|
||||
)
|
||||
|
||||
ssl_path = f"{conf_backup_path}openlitespeed/detail/ssl"
|
||||
if os.path.exists(ssl_path):
|
||||
if not os.path.exists(conf_paths['openlitespeed']['ssl']):
|
||||
public.ExecShell(f"mkdir -p {conf_paths['openlitespeed']['ssl']}")
|
||||
for ssl in os.listdir(ssl_path):
|
||||
if site_name in ssl:
|
||||
public.ExecShell(
|
||||
f"\cp -rpa {ssl_path}/{ssl} {conf_paths['openlitespeed']['ssl']}/{ssl}"
|
||||
)
|
||||
|
||||
def _restore_site_db_data(self, site_db_record: dict) -> int:
|
||||
sql = db.Sql()
|
||||
sql.table('sites')
|
||||
if 'id' in site_db_record:
|
||||
del site_db_record['id']
|
||||
# SQL关键字字段
|
||||
if 'index' in site_db_record:
|
||||
del site_db_record['index']
|
||||
if_exist = sql.where(
|
||||
'name=? AND project_type=?',
|
||||
(site_db_record['name'], site_db_record['project_type'])
|
||||
).find()
|
||||
if if_exist:
|
||||
return if_exist['id']
|
||||
|
||||
# insert db record
|
||||
try:
|
||||
new_id = sql.insert(site_db_record)
|
||||
return new_id
|
||||
except Exception as e:
|
||||
raise public.lang("Site database insert failed: {}").format(str(e))
|
||||
|
||||
def _restore_site_domian_db_data(self, pid: int, domains: list) -> None:
|
||||
domain_sql = db.Sql()
|
||||
domain_sql.table('domain')
|
||||
for domain in domains:
|
||||
try:
|
||||
if not domain_sql.where('name=?', (domain['name'],)).count():
|
||||
domain_sql.add(
|
||||
'pid, name, port, addtime',
|
||||
(pid, domain['name'], int(domain['port']), public.getDate())
|
||||
)
|
||||
except Exception as e:
|
||||
public.print_log("Domain database insert failed: {}".format(str(e)))
|
||||
continue
|
||||
|
||||
def _backup_site(self, site: dict, backupPath: str) -> None:
|
||||
try:
|
||||
if site.get("project_type", "").lower() == "php":
|
||||
find = public.M('sites').where("id=?", (site['id'],)).field('name,path,id').find()
|
||||
fileName = find['name'] + '_' + time.strftime(
|
||||
'%Y%m%d_%H%M%S', time.localtime()
|
||||
) + '.zip'
|
||||
zipName = backupPath + '/' + fileName
|
||||
if not (os.path.exists(backupPath)):
|
||||
os.makedirs(backupPath)
|
||||
tmps = '/tmp/panelExec.log'
|
||||
execStr = f"cd '{find['path']}' && zip '{zipName}' . -x .user.ini > {tmps} 2>&1"
|
||||
public.ExecShell(execStr)
|
||||
public.M('backup').add(
|
||||
'type,name,pid,filename,size,addtime',
|
||||
(0, fileName, find['id'], zipName, 0, public.getDate())
|
||||
)
|
||||
elif "wp" in site.get("project_type", "").lower():
|
||||
bak_obj = wpbackup(int(site['id']))
|
||||
bak_obj.backup_full()
|
||||
except:
|
||||
pass
|
||||
|
||||
def restore_site_data(self, timestamp: str) -> None:
|
||||
"""还原站点数据
|
||||
Args:
|
||||
timestamp: 备份时间戳
|
||||
"""
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
site_backup_path = self.base_path + "/{timestamp}_backup/site/".format(timestamp=timestamp)
|
||||
# 还原site环境配置, 全局配置
|
||||
self.restore_site_config(site_backup_path)
|
||||
|
||||
if not os.path.exists(site_backup_path):
|
||||
self.print_log(public.lang("Site backup directory does not exist: {}").format(site_backup_path), 'restore')
|
||||
return
|
||||
self.print_log("====================================================", "restore")
|
||||
self.print_log(public.lang("Start restoring site data"), 'restore')
|
||||
|
||||
backupPath = public.M('config').where('id=?', (1,)).getField('backup_path')
|
||||
backupPath = backupPath + '/site/' if backupPath else "/www/backup/site/"
|
||||
with app.app_context():
|
||||
for site in restore_data['data_list']['site']:
|
||||
log_str = public.lang("Restoring {} project: {}").format(site.get("project_type"), site.get("name"))
|
||||
try:
|
||||
site_name = site['name']
|
||||
site['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
self.print_log(log_str, 'restore')
|
||||
if self.overwrite:
|
||||
# site backup if overwrite
|
||||
self._backup_site(site, backupPath)
|
||||
|
||||
# data
|
||||
if not self.overwrite and 'site_db_record' in site:
|
||||
site_id = self._restore_site_db_data(site['site_db_record'])
|
||||
if site_id and 'domains' in site:
|
||||
# 还原域名记录
|
||||
self._restore_site_domian_db_data(site_id, site['domains'])
|
||||
|
||||
# site file
|
||||
site_path = str(site['path']).rstrip('/')
|
||||
last_path: str = os.path.basename(site_path) if site['last_path'] == '' else site['last_path'] # site name
|
||||
# site abs path
|
||||
site_zip = site_backup_path + last_path + ".zip"
|
||||
|
||||
if os.path.exists(site_zip):
|
||||
public.ExecShell(f"cd {site_backup_path} && unzip -o {last_path}.zip")
|
||||
|
||||
site_data_path = site_backup_path + last_path # site unzip file
|
||||
if os.path.exists(site_data_path):
|
||||
site_parent_path = os.path.dirname(site_path) # /www/wwwroot
|
||||
if not os.path.exists(site_parent_path):
|
||||
public.ExecShell("mkdir -p {}".format(site_parent_path))
|
||||
public.ExecShell("chown -R www:www {}".format(site_parent_path))
|
||||
public.ExecShell("chmod -R 755 {}".format(site_parent_path))
|
||||
|
||||
src_site = os.path.join(site_backup_path, last_path)
|
||||
dst_site = os.path.join(site_parent_path, last_path)
|
||||
|
||||
public.print_log('copying site directory from {} to {}'.format(src_site, dst_site))
|
||||
|
||||
self.copy_directory(
|
||||
src=src_site,
|
||||
dst=dst_site,
|
||||
overwrite=self.overwrite,
|
||||
)
|
||||
try:
|
||||
shutil.rmtree(src_site)
|
||||
except Exception as e:
|
||||
public.print_log(public.lang("Failed to delete source site directory: {}").format(str(e)))
|
||||
|
||||
# makesure
|
||||
public.ExecShell(f"chown -R www:www {dst_site}")
|
||||
user_ini = dst_site + "/.user.ini"
|
||||
if not os.path.exists(user_ini) or self.overwrite:
|
||||
public.writeFile(user_ini, f"open_basedir={site_parent_path}/:/tmp/")
|
||||
public.ExecShell("chmod 644 " + user_ini)
|
||||
public.ExecShell("chown root:root " + user_ini)
|
||||
public.ExecShell("chattr +i " + user_ini)
|
||||
|
||||
# site config
|
||||
site_conf_zip = site_backup_path + site_name + "_conf.zip"
|
||||
if os.path.exists(site_conf_zip):
|
||||
public.ExecShell(
|
||||
"cd {site_backup_path} && unzip -o {site_name}_conf.zip".format(
|
||||
site_backup_path=site_backup_path, site_name=site_name
|
||||
)
|
||||
)
|
||||
public.ExecShell(
|
||||
"cd {site_backup_path} && \cp -rpa {site_name}_conf/* /www/server/panel/vhost".format(
|
||||
site_backup_path=site_backup_path, site_name=site_name
|
||||
)
|
||||
)
|
||||
|
||||
new_log_str = public.lang("{} project: {} ✓").format(site['project_type'], site['name'])
|
||||
self.replace_log(log_str, new_log_str, 'restore')
|
||||
site['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
except Exception as e:
|
||||
site['restore_status'] = 3
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
new_log_str = public.lang(f"{site['project_type']} project: {site['name']} Reason: {str(e)}")
|
||||
self.replace_log(log_str, new_log_str, 'restore')
|
||||
continue
|
||||
|
||||
self.print_log(public.lang("Site data restoration completed"), 'restore')
|
||||
# 还原site 所有Python环境
|
||||
# self.restore_site_python_env(timestamp)
|
||||
|
||||
def backup_site_dir_auth(self, site_name: str):
|
||||
if os.path.exists(self.site_dir_auth_path):
|
||||
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
|
||||
if site_name in site_dir_auth_data:
|
||||
result = {site_name: site_dir_auth_data[site_name]}
|
||||
return result
|
||||
return False
|
||||
|
||||
def restore_site_dir_auth(self, site_name: str, backup_data_path: str):
|
||||
if os.path.exists(backup_data_path):
|
||||
dir_auth_backup_data = json.loads(public.ReadFile(backup_data_path))
|
||||
if os.path.exists(self.site_dir_auth_path):
|
||||
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
|
||||
site_dir_auth_data[site_name] = dir_auth_backup_data[site_name]
|
||||
public.WriteFile(self.site_dir_auth_path, json.dumps(site_dir_auth_data))
|
||||
|
||||
def backup_dir_pass(self, site_name: str, backup_data_path: str):
|
||||
if os.path.exists(self.site_dir_auth_path):
|
||||
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
|
||||
if site_name in site_dir_auth_data:
|
||||
result = {site_name: site_dir_auth_data[site_name]}
|
||||
return result
|
||||
return {}
|
||||
|
||||
def backup_redirect_conf(self, site_name: str):
|
||||
if os.path.exists(self.redirect_conf_path):
|
||||
redirect_conf_data = json.loads(public.ReadFile(self.redirect_conf_path))
|
||||
for item in redirect_conf_data:
|
||||
if site_name in item['sitename']:
|
||||
return item
|
||||
return False
|
||||
|
||||
def restore_redirect_conf(self, site_name: str, backup_data_path: str):
|
||||
if os.path.exists(backup_data_path):
|
||||
redirect_conf_data = json.loads(public.ReadFile(backup_data_path))
|
||||
local_redirect_conf_data = []
|
||||
if os.path.exists(self.redirect_conf_path):
|
||||
local_redirect_conf_data = json.loads(public.ReadFile(self.redirect_conf_path))
|
||||
data_exists = None
|
||||
for item in local_redirect_conf_data:
|
||||
if item['sitename'] == redirect_conf_data['sitename']:
|
||||
data_exists = True
|
||||
if not data_exists:
|
||||
local_redirect_conf_data.append(redirect_conf_data)
|
||||
public.WriteFile(self.redirect_conf_path, json.dumps(local_redirect_conf_data))
|
||||
return False
|
||||
|
||||
def backup_proxy_conf(self, site_name: str):
|
||||
if os.path.exists(self.proxy_conf_path):
|
||||
proxy_conf_data = json.loads(public.ReadFile(self.proxy_conf_path))
|
||||
for item in proxy_conf_data:
|
||||
if site_name in item['sitename']:
|
||||
return item
|
||||
return False
|
||||
|
||||
def restore_proxy_conf(self, site_name: str, backup_data_path: str):
|
||||
if os.path.exists(backup_data_path):
|
||||
proxy_conf_data = json.loads(public.ReadFile(backup_data_path))
|
||||
local_proxy_conf_data = []
|
||||
if os.path.exists(self.proxy_conf_path):
|
||||
local_proxy_conf_data = json.loads(public.ReadFile(self.proxy_conf_path))
|
||||
data_exists = None
|
||||
for item in local_proxy_conf_data:
|
||||
if item['sitename'] == proxy_conf_data['sitename']:
|
||||
data_exists = True
|
||||
if not data_exists:
|
||||
local_proxy_conf_data.append(proxy_conf_data)
|
||||
public.WriteFile(self.proxy_conf_path, json.dumps(local_proxy_conf_data))
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # IP地址
|
||||
site_module = SiteModule() # 实例化对象
|
||||
if hasattr(site_module, method_name): # 检查方法是否存在
|
||||
method = getattr(site_module, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' 'does not exist'")
|
||||
829
mod/project/backup_restore/modules/soft_module.py
Normal file
829
mod/project/backup_restore/modules/soft_module.py
Normal file
@@ -0,0 +1,829 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from YakPanel import app
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
OFFICIAL_URL = public.OfficialDownloadBase()
|
||||
|
||||
|
||||
class SoftModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.packet = False
|
||||
|
||||
def get_install_type(self):
|
||||
if os.path.exists("/usr/bin/yum") or os.path.exists("/usr/bin/dnf") or os.path.exists("/usr/sbin/yum"):
|
||||
return 1
|
||||
elif os.path.exists(
|
||||
"/usr/bin/apt"
|
||||
) or os.path.exists(
|
||||
"/usr/sbin/apt-get"
|
||||
) or os.path.exists(
|
||||
"/usr/bin/apt-get"
|
||||
):
|
||||
return 4
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_web_server(self):
|
||||
if os.path.exists("/www/server/nginx/sbin/nginx"):
|
||||
nginx_version = public.ExecShell("nginx -v 2>&1")[0].replace("\n", "")
|
||||
version_match = re.search(r'nginx/(\d+\.\d+)', nginx_version)
|
||||
if version_match:
|
||||
nginx_version = version_match.group(1)
|
||||
|
||||
result = {
|
||||
"name": "nginx",
|
||||
"version": nginx_version,
|
||||
"size": BaseUtil().get_file_size("/www/server/nginx/"),
|
||||
"status": 2,
|
||||
}
|
||||
self.print_log("nginx {} ✓".format(nginx_version), 'backup')
|
||||
return result
|
||||
|
||||
if os.path.exists("/www/server/apache/bin/httpd"):
|
||||
apache_version = public.ExecShell("httpd -v 2>&1")[0].replace("\n", "")
|
||||
version_match = re.search(r'Apache/(\d+\.\d+)', apache_version)
|
||||
if version_match:
|
||||
apache_version = version_match.group(1)
|
||||
|
||||
result = {
|
||||
"name": "apache",
|
||||
"version": apache_version,
|
||||
"size": BaseUtil().get_file_size("/www/server/apache/"),
|
||||
"status": 2,
|
||||
}
|
||||
self.print_log("apache {} ✓".format(apache_version), 'backup')
|
||||
return result
|
||||
|
||||
if os.path.exists("/usr/local/lsws/bin/openlitespeed"):
|
||||
openlitespeed_version = public.ExecShell(
|
||||
"/usr/local/lsws/bin/openlitespeed -v 2>&1")[0].replace("\n", "")
|
||||
version_match = re.search(r'LiteSpeed/(\d+\.\d+\.\d+) Open', openlitespeed_version)
|
||||
if version_match:
|
||||
openlitespeed_version = version_match.group(1)
|
||||
|
||||
result = {
|
||||
"name": "openlitespeed",
|
||||
"version": openlitespeed_version,
|
||||
"size": BaseUtil().get_file_size("/usr/local/lsws/"),
|
||||
"status": 2,
|
||||
}
|
||||
self.print_log("openlitespeed {} ✓".format(openlitespeed_version), 'backup')
|
||||
return result
|
||||
|
||||
def get_php_server(self):
|
||||
php_dir = "/www/server/php"
|
||||
if os.path.exists(php_dir):
|
||||
phplist = []
|
||||
for dir_name in os.listdir(php_dir):
|
||||
dir_path = dir_path = os.path.join(php_dir, dir_name)
|
||||
if os.path.isdir(dir_path) and os.path.exists(os.path.join(dir_path, 'bin/php')):
|
||||
phplist.append(int(dir_name))
|
||||
|
||||
result = []
|
||||
for php_ver in phplist:
|
||||
php_ext = public.ExecShell("/www/server/php/{}/bin/php -m".format(php_ver))[0].split("\n")
|
||||
filtered_data = [item for item in php_ext if item not in ('[PHP Modules]', '[Zend Modules]', '')]
|
||||
php_result = {
|
||||
"name": "php",
|
||||
"version": php_ver,
|
||||
"php_ext": filtered_data,
|
||||
"size": BaseUtil().get_file_size("/www/server/php/{}".format(php_ver)),
|
||||
"status": 2,
|
||||
}
|
||||
# 将PHP版本号转换为带小数点的格式
|
||||
if isinstance(php_ver, (int, str)) and len(str(php_ver)) == 2:
|
||||
# 例如:54 -> 5.4, 70 -> 7.0
|
||||
php_result['version'] = f"{str(php_ver)[0]}.{str(php_ver)[1]}"
|
||||
elif isinstance(php_ver, (int, str)) and len(str(php_ver)) == 3:
|
||||
# 例如:82 -> 8.2
|
||||
php_result['version'] = f"{str(php_ver)[0]}.{str(php_ver)[1:]}"
|
||||
result.append(php_result)
|
||||
self.print_log("php {} ✓".format(php_result['version']), 'backup')
|
||||
return result
|
||||
return None
|
||||
|
||||
def get_mysql_server(self):
|
||||
if os.path.exists("/www/server/mysql/bin/mysql"):
|
||||
mysql_version = None
|
||||
if os.path.exists("/www/server/mysql/version.pl"):
|
||||
mysql_version = public.ReadFile("/www/server/mysql/version.pl").replace("\n", "")
|
||||
elif os.path.exists("/www/server/mysql/version_check.pl"):
|
||||
mysql_version = public.ExecShell("/www/server/mysql/version_check.pl")[0].replace("\n", "")
|
||||
|
||||
match = re.search(r'10\.\d+', mysql_version)
|
||||
if match:
|
||||
version = match.group()
|
||||
type = "mariadb"
|
||||
mysql_version = version
|
||||
else:
|
||||
type = "mysql"
|
||||
mysql_version = mysql_version[0:3]
|
||||
result = {
|
||||
"type": type,
|
||||
"version": mysql_version,
|
||||
"size": BaseUtil().get_file_size("/www/server/mysql/"),
|
||||
"status": 2,
|
||||
}
|
||||
self.print_log("mysql {} ✓".format(mysql_version), 'backup')
|
||||
return result
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_ftp_server(self, get=None):
|
||||
if os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
|
||||
size = BaseUtil().get_file_size("/www/server/pure-ftpd/")
|
||||
try:
|
||||
pure_ftp_port = \
|
||||
public.ExecShell("cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep Bind|awk '{print $2}'")[
|
||||
0].replace("\n", "").replace("0.0.0.0,", "")
|
||||
pure_ftp_port = int(pure_ftp_port)
|
||||
except:
|
||||
pure_ftp_port = 21
|
||||
self.print_log("pure-ftpd {} ✓".format(pure_ftp_port), 'backup')
|
||||
return {
|
||||
"name": "pure-ftpd",
|
||||
"version": "1.0.49",
|
||||
"size": size,
|
||||
"port": int(pure_ftp_port),
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_node_list(self, timestamp):
|
||||
node_dir = "/www/server/nodejs"
|
||||
if not os.path.exists(node_dir):
|
||||
return None
|
||||
node_list = []
|
||||
|
||||
result = []
|
||||
for dir_name in os.listdir(node_dir):
|
||||
if re.match(r"^v[1-9]\d*(\.\d+)*$", dir_name):
|
||||
node_list.append(dir_name)
|
||||
|
||||
for node_ver in node_list:
|
||||
node_ver_path = os.path.join(node_dir, node_ver)
|
||||
node_mod_path = os.path.join(node_ver_path, "lib", "node_modules")
|
||||
if os.path.isdir(node_mod_path):
|
||||
mod_list = os.listdir(node_mod_path)
|
||||
else:
|
||||
mod_list = []
|
||||
node_result = {
|
||||
"name": "node",
|
||||
"version": node_ver,
|
||||
"mod_list": mod_list,
|
||||
"size": BaseUtil().get_file_size("/www/server/nodejs/{}".format(node_ver)),
|
||||
"status": 2,
|
||||
}
|
||||
result.append(node_result)
|
||||
self.print_log("node {} ✓".format(node_ver), 'backup')
|
||||
|
||||
if result and self.packet:
|
||||
backup_path = os.path.join(self.base_path, f"{timestamp}_backup/plugin")
|
||||
public.ExecShell(f"\cp -rpa /www/server/nodejs/* {backup_path}/nodejs/*")
|
||||
return result
|
||||
|
||||
def get_redis_server(self):
|
||||
if os.path.exists("/www/server/redis/src/redis-server") and os.path.exists("/www/server/redis/version.pl"):
|
||||
redis_version = public.ReadFile("/www/server/redis/version.pl")
|
||||
size = BaseUtil().get_file_size("/www/server/redis/")
|
||||
self.print_log("redis {} ✓".format(redis_version[0:3]), 'backup')
|
||||
return {
|
||||
"name": "redis",
|
||||
"version": redis_version[0:3],
|
||||
"size": size,
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_memcached_server(self):
|
||||
if os.path.exists("/usr/local/memcached/bin/memcached"):
|
||||
size = BaseUtil().get_file_size("/usr/local/memcached/")
|
||||
self.print_log("memcached {} ✓".format("1.6.12"), 'backup')
|
||||
return {
|
||||
"name": "memcached",
|
||||
"version": "1.6.12",
|
||||
"size": size,
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_mongodb_server(self):
|
||||
if os.path.exists("/www/server/mongodb/version.pl"):
|
||||
mongod = "/www/server/mongodb/bin/mongod"
|
||||
mongo = "/www/server/mongodb/bin/mongo"
|
||||
if os.path.exists(mongod) or os.path.exists(mongo):
|
||||
mongodb_version = public.ReadFile("/www/server/mongodb/version.pl")
|
||||
size = BaseUtil().get_file_size("/www/server/mongodb/")
|
||||
self.print_log("mongodb {} ✓".format(mongodb_version[0:3]), 'backup')
|
||||
return {
|
||||
"name": "mongodb",
|
||||
"version": mongodb_version[0:3],
|
||||
"size": size,
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_pgsql_server(self):
|
||||
if os.path.exists("/www/server/pgsql/bin/pg_config"):
|
||||
pgsql_version = \
|
||||
public.ExecShell("/www/server/pgsql/bin/pg_config --version")[0].replace("\n", "").split(" ")[1]
|
||||
size = BaseUtil().get_file_size("/www/server/pgsql/")
|
||||
self.print_log("pgsql {} ✓".format(pgsql_version), 'backup')
|
||||
return {
|
||||
"name": "pgsql",
|
||||
"version": pgsql_version,
|
||||
"size": size,
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_phpmyadmin_version(self):
|
||||
if os.path.exists("/www/server/phpmyadmin/version.pl"):
|
||||
phpmyadmin_version = public.ReadFile("/www/server/phpmyadmin/version.pl").replace("\n", "")
|
||||
size = BaseUtil().get_file_size("/www/server/phpmyadmin/")
|
||||
self.print_log("phpmyadmin {} ✓".format(phpmyadmin_version), 'backup')
|
||||
return {
|
||||
"name": "phpmyadmin",
|
||||
"version": phpmyadmin_version,
|
||||
"size": size,
|
||||
"status": 2,
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_soft_data(self, timestamp=None, packet: bool = False):
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Start backing up software information"), "backup")
|
||||
self.packet = packet
|
||||
result = {
|
||||
"web_server": self.get_web_server(),
|
||||
"php_server": self.get_php_server(),
|
||||
"mysql_server": self.get_mysql_server(),
|
||||
"ftp_server": self.get_ftp_server(),
|
||||
# "node_list": self.get_node_list(timestamp),
|
||||
"redis_server": self.get_redis_server(),
|
||||
"memcached_server": self.get_memcached_server(),
|
||||
"mongodb_server": self.get_mongodb_server(),
|
||||
"pgsql_server": self.get_pgsql_server(),
|
||||
"phpmyadmin_version": self.get_phpmyadmin_version(),
|
||||
}
|
||||
public.WriteFile("/root/soft.json", json.dumps(result))
|
||||
self.print_log(public.lang("Software information backup completed"), 'backup')
|
||||
return result
|
||||
|
||||
# ======================== install software ========================
|
||||
def install_web_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
web_server = restore_data['data_list']['soft']['web_server']
|
||||
install_type = self.get_install_type()
|
||||
log_str = public.lang("Start installing nginx-{}").format(web_server.get('version', 'latest'))
|
||||
try:
|
||||
result = None
|
||||
if web_server['name'] == 'nginx':
|
||||
self.print_log(log_str, "restore")
|
||||
web_server['restore_status'] = 1
|
||||
web_server['msg'] = None
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O nginx.sh {}/install/{}/nginx.sh && bash nginx.sh install {}".format(
|
||||
OFFICIAL_URL, install_type, web_server['version']
|
||||
)
|
||||
)
|
||||
elif web_server['name'] == 'apache':
|
||||
self.print_log(public.lang("Start installing apache service"), "restore")
|
||||
web_server['restore_status'] = 1
|
||||
web_server['msg'] = None
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O apache.sh {}/install/{}/apache.sh && bash apache.sh install {}".format(
|
||||
OFFICIAL_URL, install_type, web_server['version']
|
||||
)
|
||||
)
|
||||
if web_server['name'] == 'nginx' and os.path.exists("/www/server/nginx/sbin/nginx"):
|
||||
new_log_str = "{}-{} ✓".format(web_server['name'], web_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
web_server['restore_status'] = 2
|
||||
web_server['msg'] = None
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
elif web_server['name'] == 'apache' and os.path.exists("/www/server/apache/bin/httpd"):
|
||||
new_log_str = "{}-{} ✓".format(web_server['name'], web_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
web_server['restore_status'] = 2
|
||||
web_server['msg'] = None
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"{}-{} ✗ Installation failed Reason: {} \n Please try to reinstall the web server in the software store after the restore task ends").format(
|
||||
web_server['name'], web_server['version'], err_msg
|
||||
)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
web_server['restore_status'] = 3
|
||||
web_server['msg'] = new_log_str
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
except Exception as e:
|
||||
err_msg = public.lang(
|
||||
"{}-{} ✗ Installation failed Reason: {} \n Please try to reinstall the web server in the software store after the restore task ends").format(
|
||||
web_server['name'], web_server['version'], str(e)
|
||||
)
|
||||
web_server['restore_status'] = 3
|
||||
web_server['msg'] = err_msg
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
self.print_log(public.lang("Web server installation completed"), "restore")
|
||||
|
||||
def install_php_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
php_server = restore_data['data_list']['soft']['php_server']
|
||||
install_type = self.get_install_type()
|
||||
for php in php_server:
|
||||
php_ver = php['version']
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
log_str = public.lang("Start installing php-{}").format(php_ver)
|
||||
self.print_log(log_str, "restore")
|
||||
path_ver = php_ver.replace('.', '')
|
||||
if os.path.exists("/www/server/php/{}".format(path_ver)):
|
||||
new_log_str = "php-{} ✓".format(php_ver)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
continue
|
||||
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O php.sh {}/install/{}/php.sh && bash php.sh install {}".format(
|
||||
OFFICIAL_URL, install_type, php_ver
|
||||
)
|
||||
)
|
||||
if not os.path.exists("/www/server/php/{}".format(path_ver)):
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"php-{} ✗ Installation failed Reason: {} \n Please try to reinstall php in the software store after the restore task ends").format(
|
||||
php_ver,
|
||||
err_msg)
|
||||
php["restore_status"] = 3
|
||||
php["msg"] = err_msg
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
php["restore_status"] = 2
|
||||
php["msg"] = "success"
|
||||
new_log_str = "php-{} ✓".format(php_ver)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_node(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
node_list = restore_data['data_list']['soft']['node_list']
|
||||
for node_data in node_list:
|
||||
node_ver = node_data['version']
|
||||
log_str = public.lang("Start installing node-{}").format(node_ver)
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/www/server/nodejs/{}".format(node_ver)):
|
||||
new_log_str = "node-{} ✓".format(node_ver)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
continue
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O node_plugin_install.sh {}/install/0/node_plugin_install.sh && bash node_plugin_install.sh {}".format(
|
||||
OFFICIAL_URL, node_ver
|
||||
)
|
||||
)
|
||||
|
||||
for mod_list in node_data['mod_list']:
|
||||
mod_name = mod_list
|
||||
mod_shell = '''
|
||||
export PATH
|
||||
export HOME=/root
|
||||
export NODE_PATH="/www/server/nodejs/{node_ver}/etc/node_modules"
|
||||
/www/server/nodejs/{node_ver}//bin/npm config set registry https://registry.npmmirror.com/
|
||||
/www/server/nodejs/{node_ver}//bin/npm config set prefix /www/server/nodejs/{node_ver}/
|
||||
/www/server/nodejs/{node_ver}//bin/npm config set cache /www/server/nodejs/{node_ver}//cache
|
||||
/www/server/nodejs/{node_ver}//bin/npm config set strict-ssl false
|
||||
/www/server/nodejs/{node_ver}//bin/yarn config set registry https://registry.npmmirror.com/
|
||||
/www/server/nodejs/{node_ver}/bin/npm install {mod_name} -g &> /www/server/panel/plugin/nodejs/exec.log
|
||||
'''.format(node_ver=node_ver, mod_name=mod_name)
|
||||
result = public.ExecShell(mod_shell)
|
||||
if os.path.exists("/www/server/nodejs/{}".format(node_ver)):
|
||||
new_log_str = "node-{} ✓".format(node_ver)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
node_data["restore_status"] = 2
|
||||
node_data["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"node-{} ✗ Installation failed Reason: {} \n Please try to reinstall node in the software store after the restore task ends").format(
|
||||
node_ver, err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
node_data["restore_status"] = 3
|
||||
node_data["msg"] = err_msg
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_mysql_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
mysql_server = restore_data['data_list']['soft']['mysql_server']
|
||||
install_type = self.get_install_type()
|
||||
if mysql_server['type'] == 'mariadb':
|
||||
log_str = public.lang("Start installing mariadb-{}").format(mysql_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/www/server/mysql/bin/mysql"):
|
||||
new_log_str = "mariadb-{} ✓".format(mysql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O mysql.sh {}/install/{}/mysql.sh && bash mysql.sh install {}".format(
|
||||
OFFICIAL_URL, install_type, f"mariadb_{mysql_server['version']}"
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/mysql/bin/mysql"):
|
||||
new_log_str = "mariadb-{} ✓".format(mysql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mysql_server["restore_status"] = 2
|
||||
mysql_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"mariadb-{} ✗ Installation failed Reason: {} \n Please try to reinstall mariadb in the software store after the restore task ends").format(
|
||||
mysql_server['version'], err_msg
|
||||
)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mysql_server["restore_status"] = 3
|
||||
mysql_server["msg"] = err_msg
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
return
|
||||
|
||||
if mysql_server['type'] == 'mysql':
|
||||
log_str = public.lang("Start installing mysql-{}").format(mysql_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/www/server/mysql/bin/mysql"):
|
||||
new_log_str = "mysql-{} ✓".format(mysql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O mysql.sh {}/install/{}/mysql.sh && bash mysql.sh install {}".format(
|
||||
OFFICIAL_URL, install_type, mysql_server['version']
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/mysql/bin/mysql"):
|
||||
new_log_str = "mysql-{} ✓".format(mysql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mysql_server["restore_status"] = 2
|
||||
mysql_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"mysql-{} ✗ Installation failed Reason: {} \n Please try to reinstall mysql in the software store after the restore task ends").format(
|
||||
mysql_server['version'], err_msg
|
||||
)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mysql_server["restore_status"] = 3
|
||||
mysql_server["msg"] = err_msg
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_mongodb_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
mongodb_server = restore_data['data_list']['soft']['mongodb_server']
|
||||
# install_type = self.get_install_type()
|
||||
log_str = public.lang("Start installing mongodb-{}").format(mongodb_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
mongo = "/www/server/mongodb/bin/mongo"
|
||||
mongod = "/www/server/mongodb/bin/mongod"
|
||||
if (os.path.exists(mongo) or os.path.exists(mongod)) and os.path.exists("/www/server/mongodb/version.pl"):
|
||||
new_log_str = "mongodb-{} ✓".format(mongodb_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O mongodb.sh {}/install/0/mongodb.sh && bash mongodb.sh install {}".format(
|
||||
OFFICIAL_URL, mongodb_server['version']
|
||||
)
|
||||
)
|
||||
if os.path.exists(mongo) or os.path.exists(mongod):
|
||||
new_log_str = "mongodb-{} ✓".format(mongodb_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mongodb_server["restore_status"] = 2
|
||||
mongodb_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"mongodb-{} ✗ Installation failed Reason: {} \n Please try to reinstall mongodb in the software store after the restore task ends").format(
|
||||
mongodb_server['version'], err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
mongodb_server["restore_status"] = 3
|
||||
mongodb_server["msg"] = err_msg
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_memcached_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
memcached_server = restore_data['data_list']['soft']['memcached_server']
|
||||
# install_type = self.get_install_type()
|
||||
log_str = public.lang("Start installing memcached-{}").format(memcached_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/usr/local/memcached/bin/memcached"):
|
||||
new_log_str = "memcached-{} ✓".format(memcached_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
result = public.ExecShell(
|
||||
f"cd /www/server/panel/install && wget -O memcached.sh {OFFICIAL_URL}/install/0/memcached.sh && bash memcached.sh install"
|
||||
)
|
||||
if os.path.exists("/usr/local/memcached/bin/memcached"):
|
||||
new_log_str = "memcached-{} ✓".format(memcached_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
memcached_server["restore_status"] = 2
|
||||
memcached_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"memcached-{} ✗ Installation failed Reason: {} \n Please try to reinstall memcached in the software store after the restore task ends").format(
|
||||
memcached_server['version'], err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
memcached_server["restore_status"] = 3
|
||||
memcached_server["msg"] = err_msg
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_redis_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
redis_server = restore_data['data_list']['soft']['redis_server']
|
||||
# install_type = self.get_install_type()
|
||||
log_str = public.lang("Start installing redis-{}").format(redis_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/www/server/redis/src/redis-server") and os.path.exists("/www/server/redis/version.pl"):
|
||||
new_log_str = "redis-{} ✓".format(redis_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O redis.sh {}/install/0/redis.sh && bash redis.sh install {}".format(
|
||||
OFFICIAL_URL, redis_server['version']
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/redis/src/redis-cli"):
|
||||
new_log_str = "redis-{} ✓".format(redis_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
redis_server["restore_status"] = 2
|
||||
redis_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = "redis-{} ✗ {}".format(redis_server['version'], err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
redis_server["restore_status"] = 3
|
||||
redis_server["msg"] = err_msg
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_pgsql_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
pgsql_server = restore_data['data_list']['soft']['pgsql_server']
|
||||
log_str = public.lang("Start installing pgsql-{}").format(pgsql_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
if os.path.exists("/www/server/pgsql/bin/pg_config"):
|
||||
new_log_str = "pgsql-{} ✓".format(pgsql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
return
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
down_file = "postgresql-{pgsql_version}.tar.gz".format(pgsql_version=pgsql_server['version'])
|
||||
down_url = "{}/src/postgresql-{}.tar.gz".format(
|
||||
OFFICIAL_URL, pgsql_server['version']
|
||||
)
|
||||
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O pgsql_install.sh {}/install/0/pgsql_install.sh && bash pgsql_install.sh {} {}".format(
|
||||
OFFICIAL_URL, down_file, down_url
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/pgsql/bin/psql"):
|
||||
new_log_str = "pgsql-{} ✓".format(pgsql_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
pgsql_server["restore_status"] = 2
|
||||
pgsql_server["msg"] = None
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = "pgsql-{} ✗ {}".format(pgsql_server['version'], err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
pgsql_server["restore_status"] = 3
|
||||
pgsql_server["msg"] = err_msg
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_phpmyadmin(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
phpmyadmin_server = restore_data['data_list']['soft']['phpmyadmin_version']
|
||||
log_str = public.lang("Start installing phpmyadmin-{}").format(phpmyadmin_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
result = public.ExecShell(
|
||||
"cd /www/server/panel/install && wget -O phpmyadmin.sh {}/install/0/phpmyadmin.sh && bash phpmyadmin.sh install {}".format(
|
||||
OFFICIAL_URL, phpmyadmin_server['version']
|
||||
)
|
||||
)
|
||||
if os.path.exists("/www/server/phpmyadmin/version.pl"):
|
||||
phpmyadmin_server["restore_status"] = 2
|
||||
phpmyadmin_server["msg"] = None
|
||||
new_log_str = "phpmyadmin-{} ✓".format(phpmyadmin_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
else:
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"phpmyadmin-{} ✗ Installation failed Reason: {} \n Please try to reinstall phpmyadmin in the software store after the restore task ends").format(
|
||||
phpmyadmin_server['version'], err_msg
|
||||
)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
phpmyadmin_server["restore_status"] = 3
|
||||
phpmyadmin_server["msg"] = err_msg
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
def install_ftp_server(self, timestamp):
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
ftp_server = restore_data['data_list']['soft']['ftp_server']
|
||||
log_str = public.lang("Start installing ftp-{}").format(ftp_server['version'])
|
||||
self.print_log(log_str, "restore")
|
||||
|
||||
if os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
|
||||
new_log_str = "ftp-{} ✓".format(ftp_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
ftp_server["restore_status"] = 2
|
||||
ftp_server["msg"] = None
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
return
|
||||
|
||||
result = public.ExecShell(
|
||||
f"cd /www/server/panel/install && wget -O pureftpd.sh {OFFICIAL_URL}/install/0/pureftpd.sh && bash pureftpd.sh install"
|
||||
)
|
||||
public.ExecShell("rm -f /www/server/pure-ftpd/etc/pureftpd.passwd")
|
||||
public.ExecShell("rm -f /www/server/pure-ftpd/etc/pureftpd.pdb")
|
||||
if not os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
|
||||
combined_output = (result[0] + result[1]).splitlines()
|
||||
err_msg = '\n'.join(combined_output[-10:])
|
||||
new_log_str = public.lang(
|
||||
"ftp-{} ✗ Installation failed Reason: {} \nPlease try to reinstall ftp in the software store after the restore task ends").format(
|
||||
ftp_server['version'], err_msg)
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
ftp_server["restore_status"] = 3
|
||||
ftp_server["msg"] = err_msg
|
||||
else:
|
||||
new_log_str = "ftp-{} ✓".format(ftp_server['version'])
|
||||
self.replace_log(log_str, new_log_str, "restore")
|
||||
ftp_server["restore_status"] = 2
|
||||
ftp_server["msg"] = None
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
import ftp
|
||||
if ftp_server['port'] != 21:
|
||||
with app.app_context():
|
||||
args = public.dict_obj()
|
||||
args.port = str(ftp_server['port'])
|
||||
ftp.ftp().setPort(args)
|
||||
|
||||
def restore_env(self, timestamp):
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Start restoring panel running environment"), "restore")
|
||||
self.print_log(public.lang("Will skip installation if the same environment exists"), "restore")
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
soft_json_data = restore_data['data_list']['soft']
|
||||
try:
|
||||
if soft_json_data['web_server']:
|
||||
self.install_web_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing web server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['php_server']:
|
||||
self.install_php_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing PHP server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['mysql_server']:
|
||||
self.install_mysql_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing MySQL server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['ftp_server']:
|
||||
self.install_ftp_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing FTP server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
# try:
|
||||
# if soft_json_data['node_list']:
|
||||
# self.install_node(timestamp)
|
||||
# except Exception as e:
|
||||
# import traceback
|
||||
# public.print_log(traceback.format_exc())
|
||||
# public.print_log("Error installing Node.js: {}".format(str(e)))
|
||||
# pass
|
||||
|
||||
try:
|
||||
if soft_json_data['redis_server']:
|
||||
self.install_redis_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing Redis server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['memcached_server']:
|
||||
self.install_memcached_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing Memcached server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['mongodb_server']:
|
||||
self.install_mongodb_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing MongoDB server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['pgsql_server']:
|
||||
self.install_pgsql_server(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing PostgreSQL server: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
if soft_json_data['phpmyadmin_version']:
|
||||
self.install_phpmyadmin(timestamp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log(traceback.format_exc())
|
||||
public.print_log("Error installing phpMyAdmin: {}".format(str(e)))
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名 p
|
||||
timestamp = sys.argv[2]
|
||||
soft_manager = SoftModule() # 实例化对象
|
||||
if hasattr(soft_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(soft_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")
|
||||
92
mod/project/backup_restore/modules/ssh_module.py
Normal file
92
mod/project/backup_restore/modules/ssh_module.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <miku@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from mod.project.backup_restore.base_util import BaseUtil
|
||||
from mod.project.backup_restore.config_manager import ConfigManager
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class SshModule(BaseUtil, ConfigManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.ssh_path = "/www/server/panel/config/ssh_info"
|
||||
|
||||
def backup_ssh_data(self, timestamp):
|
||||
self.print_log("====================================================", "backup")
|
||||
self.print_log(public.lang("Start backing up terminal data"), "backup")
|
||||
|
||||
ssh_backup_path = self.base_path + "/{timestamp}_backup/ssh".format(timestamp=timestamp)
|
||||
if not os.path.exists(ssh_backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(ssh_backup_path))
|
||||
print(self.ssh_path)
|
||||
public.ExecShell("\cp -rpa {} {}".format(self.ssh_path, ssh_backup_path))
|
||||
|
||||
ssh_info = {
|
||||
'status': 2,
|
||||
'msg': None,
|
||||
'ssh_info_path': ssh_backup_path,
|
||||
}
|
||||
|
||||
backup_size = self.format_size(self.get_file_size(ssh_backup_path))
|
||||
self.print_log(public.lang("Terminal data backup completed. Data size: {}").format(backup_size), 'backup')
|
||||
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
data_list['data_list']['ssh'] = ssh_info
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
|
||||
def restore_ssh_data(self, timestamp):
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Start restoring terminal data"), "restore")
|
||||
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
ssh_data = restore_data['data_list']['ssh']
|
||||
ssh_info_path = ssh_data['ssh_info_path'] + "/ssh_info"
|
||||
|
||||
restore_data['data_list']['ssh']['restore_status'] = 1
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
if not os.path.exists(ssh_info_path):
|
||||
self.print_log(public.lang("Restore failed, file does not exist"), "restore")
|
||||
return
|
||||
if not os.path.exists(self.ssh_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.ssh_path))
|
||||
public.ExecShell("\cp -rpa {}/* {}".format(ssh_info_path, self.ssh_path))
|
||||
self.print_log(public.lang("Terminal data restoration completed"), "restore")
|
||||
|
||||
restore_data['data_list']['ssh']['restore_status'] = 2
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2]
|
||||
ssh_manager = SshModule() # 实例化对象
|
||||
if hasattr(ssh_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(ssh_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: Method '{method_name}' does not exist")
|
||||
343
mod/project/backup_restore/modules/ssl_model.py
Normal file
343
mod/project/backup_restore/modules/ssl_model.py
Normal file
@@ -0,0 +1,343 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
import public
|
||||
from public.hook_import import hook_import
|
||||
|
||||
hook_import()
|
||||
from YakPanel import app
|
||||
from ssl_domainModelV2.api import DomainObject
|
||||
from ssl_domainModelV2.service import CertHandler
|
||||
from ssl_domainModelV2.config import UserFor
|
||||
from ssl_domainModelV2.model import DnsDomainSSL, DnsDomainProvider
|
||||
from mod.project.backup_restore.data_manager import DataManager
|
||||
|
||||
|
||||
class SSLModel(DataManager):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
|
||||
def get_ssl_backup_conf(self, timestamp: int = None) -> dict:
|
||||
"""
|
||||
Get SSL certificate and DNS API provider backup configuration
|
||||
"""
|
||||
ssl_list = [
|
||||
{
|
||||
**ssl.as_dict(),
|
||||
"data_type": "backup",
|
||||
"status": 0,
|
||||
"msg": None,
|
||||
} for ssl in DnsDomainSSL.objects.filter(is_order=0) # 过滤商业证书
|
||||
]
|
||||
provider_list = [
|
||||
{
|
||||
**x.as_dict(),
|
||||
# 与备份的 status 字段冲突, 使用 account status
|
||||
"account_status": x.status,
|
||||
"data_type": "backup",
|
||||
"status": 0,
|
||||
"msg": None,
|
||||
} for x in DnsDomainProvider.objects.all()
|
||||
]
|
||||
res = {
|
||||
"ssl_list": ssl_list,
|
||||
"provider_list": provider_list,
|
||||
}
|
||||
return res
|
||||
|
||||
def backup_ssl_data(self, timestamp) -> None:
|
||||
"""
|
||||
Backup domain management center
|
||||
"""
|
||||
# 总配置
|
||||
data_list = self.get_backup_data_list(timestamp)
|
||||
if not data_list:
|
||||
return None
|
||||
|
||||
data_backup_path = data_list.get("backup_path")
|
||||
ssl_backup_path = Path(data_backup_path) / "ssl"
|
||||
ssl_backup_path.mkdir(parents=True, exist_ok=True)
|
||||
self.print_log("==================================", "backup")
|
||||
self.print_log(public.lang("Start backing up SSL certificate information"), "backup")
|
||||
|
||||
for ssl in data_list['data_list']['ssl'].get("ssl_list", []): # SSL in the general configuration
|
||||
try:
|
||||
if not ssl.get("path") or not os.path.exists(ssl.get("path")):
|
||||
err = public.lang("{} {} Certificate file does not exist ✗").format(
|
||||
ssl['info'].get("issuer_O", ""), ssl['dns']
|
||||
)
|
||||
self.print_log(err, "backup")
|
||||
ssl["status"] = 3
|
||||
ssl["msg"] = err
|
||||
continue
|
||||
if ssl.get("not_after_ts") < time.time() * 1000:
|
||||
err = public.lang("{} [{}] Certificate has expired ✗").format(
|
||||
ssl['info'].get("issuer_O", ""), str(ssl['dns'])
|
||||
)
|
||||
self.print_log(err, "backup")
|
||||
ssl["status"] = 3
|
||||
ssl["msg"] = err
|
||||
continue
|
||||
domian_path = ssl_backup_path / ssl.get("hash")
|
||||
CertHandler.make_last_info(domian_path, force=True)
|
||||
public.ExecShell(f"\cp -rpa {ssl['path']} {domian_path}")
|
||||
ssl["status"] = 2
|
||||
self.print_log(public.lang("{} {} ✓").format(
|
||||
ssl['info'].get("issuer_O", ""), ssl['dns']
|
||||
), "backup")
|
||||
except Exception as e:
|
||||
err = public.lang("{} {} Backup failed: {} ✗").format(
|
||||
ssl['info'].get('issuer_O', ''), ssl['dns'], str(e)
|
||||
)
|
||||
ssl["status"] = 3
|
||||
ssl["msg"] = err
|
||||
self.print_log(err, "backup")
|
||||
continue
|
||||
|
||||
new_provider_info = [
|
||||
{**x, "status": 2} for x in data_list['data_list']['ssl'].get("provider_list", [])
|
||||
]
|
||||
data_list['data_list']['ssl']['provider_list'] = new_provider_info
|
||||
self.print_log(public.lang("DNS API provider information backup completed"), "backup")
|
||||
|
||||
self.update_backup_data_list(timestamp, data_list)
|
||||
self.print_log(public.lang("SSL certificate information backup completed"), "backup")
|
||||
|
||||
def _rebuild_deploy(self, ssl_obj: DnsDomainSSL, backup_ssl: dict) -> None:
|
||||
try:
|
||||
def r_log(log_str: str, new_log: str):
|
||||
self.replace_log(log_str, new_log, "restore")
|
||||
|
||||
used = backup_ssl.get("user_for", {})
|
||||
if not ssl_obj or not used:
|
||||
return
|
||||
|
||||
# pre clear
|
||||
for other_ssl in DnsDomainSSL.objects.filter(hash__ne=ssl_obj.hash):
|
||||
is_change = False
|
||||
|
||||
for site_name in used.get(UserFor.sites, []):
|
||||
if site_name in other_ssl.sites_uf:
|
||||
other_ssl.sites_uf.remove(site_name)
|
||||
is_change = True
|
||||
|
||||
for mail_name in used.get(UserFor.mails, []):
|
||||
if mail_name in other_ssl.mails_uf:
|
||||
other_ssl.mails_uf.remove(mail_name)
|
||||
is_change = True
|
||||
|
||||
for panel_name in used.get(UserFor.panel, []):
|
||||
if panel_name in other_ssl.panel_uf:
|
||||
other_ssl.panel_uf = []
|
||||
is_change = True
|
||||
|
||||
if is_change:
|
||||
other_ssl.save()
|
||||
|
||||
if used.get(UserFor.sites):
|
||||
log_str = public.lang("Restoring deployment sites for certificate {}...").format(backup_ssl['subject'])
|
||||
self.print_log(log_str, "restore")
|
||||
build_sites = ssl_obj.deploy_sites(
|
||||
site_names=used[UserFor.sites], replace=True
|
||||
)
|
||||
r_log(log_str, public.lang("Restored deployment sites for certificate {}: {}").format(
|
||||
backup_ssl['subject'], build_sites.get('msg')
|
||||
))
|
||||
|
||||
if used.get(UserFor.mails):
|
||||
log_str = public.lang("Restoring deployment mailboxes for certificate {}...").format(
|
||||
backup_ssl['subject'])
|
||||
self.print_log(log_str, "restore")
|
||||
build_mails = ssl_obj.deploy_mails(
|
||||
mail_names=used[UserFor.mails]
|
||||
)
|
||||
r_log(log_str, public.lang("Restored deployment mailboxes for certificate {}: {}").format(
|
||||
backup_ssl['subject'], build_mails.get('msg')
|
||||
))
|
||||
|
||||
if used.get(UserFor.panel):
|
||||
log_str = public.lang("Restoring deployment panel for certificate {}...").format(backup_ssl['subject'])
|
||||
self.print_log(log_str, "restore")
|
||||
build_panel = ssl_obj.deploy_panel(
|
||||
recover=0
|
||||
)
|
||||
r_log(log_str, public.lang("Restored deployment panel for certificate {}: {}").format(
|
||||
backup_ssl['subject'], build_panel.get('msg')
|
||||
))
|
||||
except Exception as e:
|
||||
public.print_log("rebuild deploy error: {}".format(str(e)))
|
||||
|
||||
def _restore_ssl(self, backup_ssl: dict, pem: str, key: str) -> None:
|
||||
exist_obj = DnsDomainSSL.objects.filter(
|
||||
hash=CertHandler.get_hash(cert_pem=pem)
|
||||
).first()
|
||||
if exist_obj:
|
||||
if not self.overwrite:
|
||||
return
|
||||
# overwrite
|
||||
# exist_obj.provider_id = backup_ssl["provider_id"]
|
||||
# exist_obj.not_after = backup_ssl["not_after"]
|
||||
# exist_obj.not_after_ts = backup_ssl["not_after_ts"]
|
||||
exist_obj.user_for = backup_ssl["user_for"]
|
||||
exist_obj.info = backup_ssl["info"]
|
||||
exist_obj.alarm = backup_ssl["alarm"]
|
||||
exist_obj.auto_renew = backup_ssl["auto_renew"]
|
||||
exist_obj.auth_info = backup_ssl["auth_info"]
|
||||
exist_obj.log = backup_ssl["log"]
|
||||
exist_obj.save()
|
||||
# ssl_obj = exist_obj
|
||||
else:
|
||||
try:
|
||||
insert = CertHandler().save_by_data(
|
||||
cert_pem=pem,
|
||||
private_key=key,
|
||||
new_auth_info=backup_ssl["auth_info"],
|
||||
)
|
||||
if not insert:
|
||||
raise Exception(public.lang("Certificate insertion failed, please check the log"))
|
||||
except Exception as e:
|
||||
raise Exception(public.lang(f"Certificate Restore Failed: {str(e)}"))
|
||||
|
||||
# ssl_obj = DnsDomainSSL.objects.filter(hash=insert["hash"]).first()
|
||||
|
||||
# if ssl_obj:
|
||||
# # it will update the ssl field 'user_for'
|
||||
# self._rebuild_deploy(
|
||||
# ssl_obj=ssl_obj,
|
||||
# backup_ssl=backup_ssl,
|
||||
# )
|
||||
|
||||
def _restore_provider(self, provider: dict):
|
||||
if_exist = DnsDomainProvider.objects.filter(
|
||||
name=provider["name"],
|
||||
api_user=provider.get("api_user", ""),
|
||||
api_key=provider["api_key"],
|
||||
).first()
|
||||
if if_exist:
|
||||
if self.overwrite:
|
||||
return
|
||||
return
|
||||
|
||||
res = DomainObject().create_dns_api(
|
||||
public.to_dict_obj({
|
||||
"name": provider["name"],
|
||||
"api_user": provider.get("api_user", ""),
|
||||
"api_key": provider["api_key"],
|
||||
"status": provider.get("account_status", 1),
|
||||
"permission": provider.get("permission", "-"),
|
||||
"alias": provider["alias"],
|
||||
"ps": provider["ps"],
|
||||
})
|
||||
)
|
||||
if res.get("status", 0) != 0:
|
||||
raise Exception(public.lang(
|
||||
f"Restore DNS API provider failed: {res.get('message', 'create dns api error')}"
|
||||
))
|
||||
|
||||
def restore_ssl_data(self, timestamp: int) -> None:
|
||||
""" Restore domain management center """
|
||||
self.print_log("====================================================", "restore")
|
||||
self.print_log(public.lang("Start restoring domain SSL certificate configuration"), "restore")
|
||||
restore_data = self.get_restore_data_list(timestamp)
|
||||
if not restore_data:
|
||||
self.print_log(public.lang("No restore data found"), "restore")
|
||||
return
|
||||
ssl_cert_path = Path(restore_data.get("backup_path")) / "ssl"
|
||||
if not ssl_cert_path.exists():
|
||||
self.print_log(
|
||||
public.lang("Backup directory {} does not exist, unable to restore SSL certificate information").format(
|
||||
ssl_cert_path
|
||||
), "restore")
|
||||
return
|
||||
ssl_info = restore_data["data_list"].get("ssl", {})
|
||||
with app.app_context():
|
||||
# ======================= ssl =============================
|
||||
for ssl in ssl_info.get("ssl_list", []):
|
||||
log_str = public.lang("Restoring {} Subject: {}").format(
|
||||
ssl['info'].get('issuer_O'), ssl['subject']
|
||||
)
|
||||
try:
|
||||
self.print_log(log_str, "restore")
|
||||
ssl["restore_status"] = 1
|
||||
ssl_path = ssl_cert_path / ssl["hash"]
|
||||
if not ssl_path.exists():
|
||||
raise Exception(public.lang("Certificate file does not exist"))
|
||||
pem = ssl_path / "fullchain.pem"
|
||||
key = ssl_path / "privkey.pem"
|
||||
if not pem.exists() or not key.exists():
|
||||
raise Exception(public.lang("Missing certificate or private key file"))
|
||||
self._restore_ssl(
|
||||
backup_ssl=ssl,
|
||||
pem=public.readFile(str(pem)),
|
||||
key=public.readFile(str(key)),
|
||||
)
|
||||
ssl["restore_status"] = 2
|
||||
self.replace_log(
|
||||
log_str,
|
||||
public.lang(f"Restored {ssl['info'].get('issuer_O')} Subject: {ssl['subject']} ✓ "),
|
||||
"restore"
|
||||
)
|
||||
except Exception as e:
|
||||
err_msg = public.lang(
|
||||
f"Restoring {ssl['info'].get('issuer_O', '')} Subject: {ssl['subject']} failed: {str(e)}"
|
||||
)
|
||||
ssl["restore_status"] = 3
|
||||
ssl["msg"] = str(e)
|
||||
self.replace_log(log_str, err_msg, "restore")
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
# ======================= dns provider =============================
|
||||
for provider in ssl_info.get("provider_list", []):
|
||||
log_str = public.lang(f"Restoring DNS API {provider['name']}: {provider['alias']}...")
|
||||
try:
|
||||
self.print_log(log_str, "restore")
|
||||
self._restore_provider(provider)
|
||||
time.sleep(1)
|
||||
provider["restore_status"] = 2
|
||||
self.replace_log(
|
||||
log_str,
|
||||
public.lang(f"Restored DNS API {provider['name']}: {provider['alias']} ✓ "),
|
||||
"restore"
|
||||
)
|
||||
except Exception as e:
|
||||
err_msg = public.lang(f"Restoring DNS API provider: {provider['name']} failed: {str(e)}")
|
||||
provider["restore_status"] = 3
|
||||
provider["msg"] = str(e)
|
||||
self.replace_log(log_str, err_msg, "restore")
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
self.update_restore_data_list(timestamp, restore_data)
|
||||
|
||||
self.print_log(public.lang("SSL certificate information restoration completed"), "restore")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython backup_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1]
|
||||
timestamp = sys.argv[2]
|
||||
database_module = SSLModel()
|
||||
if hasattr(database_module, method_name):
|
||||
method = getattr(database_module, method_name)
|
||||
method(timestamp)
|
||||
else:
|
||||
print(f"Error: method '{method_name}' not found")
|
||||
504
mod/project/backup_restore/restore_manager.py
Normal file
504
mod/project/backup_restore/restore_manager.py
Normal file
@@ -0,0 +1,504 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: miku <wzz@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
from mod.project.backup_restore.modules.site_module import SiteModule
|
||||
from mod.project.backup_restore.modules.database_module import DatabaseModule
|
||||
from mod.project.backup_restore.modules.ftp_module import FtpModule
|
||||
|
||||
from mod.project.backup_restore.modules.crontab_module import CrontabModule
|
||||
from mod.project.backup_restore.modules.ssh_module import SshModule
|
||||
from mod.project.backup_restore.modules.firewall_module import FirewallModule
|
||||
from mod.project.backup_restore.modules.mail_module import MailModule
|
||||
from mod.project.backup_restore.modules.ssl_model import SSLModel
|
||||
from mod.project.backup_restore.modules.plugin_module import PluginModule
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
|
||||
class RestoreManager(SiteModule, DatabaseModule, FtpModule, SSLModel, CrontabModule, SshModule,
|
||||
FirewallModule, MailModule, PluginModule):
|
||||
def __init__(self, overwrite: int = 0):
|
||||
super().__init__()
|
||||
self.base_path = '/www/backup/backup_restore'
|
||||
self.bakcup_task_json = self.base_path + '/backup_task.json'
|
||||
self.backup_log_file = self.base_path + '/backup.log'
|
||||
self.backup_pl_file = self.base_path + '/backup.pl'
|
||||
self.backup_success_file = self.base_path + '/success.pl'
|
||||
self.backup_save_config = self.base_path + '/backup_save_config.json'
|
||||
self.history_log_path = '/www/backup/backup_restore/history/log'
|
||||
self.history_info_path = '/www/backup/backup_restore/history/info'
|
||||
self.restore_log_file = self.base_path + '/restore.log'
|
||||
self.restore_pl_file = self.base_path + '/restore.pl'
|
||||
self.restore_success_file = self.base_path + '/restore_success.pl'
|
||||
self.migrate_backup_info_path = '/www/backup/backup_restore/migrate_backup_info.json'
|
||||
self.overwrite = overwrite # 强制还原标志
|
||||
|
||||
def restore_data(self, timestamp):
|
||||
"""
|
||||
还原数据
|
||||
"""
|
||||
if os.path.exists(self.restore_log_file):
|
||||
public.ExecShell("rm -f {}".format(self.restore_log_file))
|
||||
|
||||
if os.path.exists(self.restore_pl_file):
|
||||
return public.returnMsg(False, public.lang("A restore process is already running!"))
|
||||
|
||||
try:
|
||||
public.WriteFile(self.restore_pl_file, timestamp)
|
||||
backup_file = str(timestamp) + "_backup.tar.gz"
|
||||
file_names = os.listdir(self.base_path)
|
||||
for file in file_names:
|
||||
if backup_file in file:
|
||||
backup_file = file
|
||||
|
||||
if os.path.exists(self.migrate_backup_info_path):
|
||||
backup_list = []
|
||||
if os.path.exists(self.bakcup_task_json):
|
||||
backup_list = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
migrate_backup_info = json.loads(public.ReadFile(self.migrate_backup_info_path))
|
||||
backup_list.append(migrate_backup_info)
|
||||
public.ExecShell("rm -f {}".format(self.migrate_backup_info_path))
|
||||
public.WriteFile(self.bakcup_task_json, json.dumps(backup_list))
|
||||
time.sleep(1)
|
||||
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
backup_conf['restore_status'] = 1
|
||||
self.save_backup_conf(timestamp, backup_conf)
|
||||
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Start decompressing the data package"), "restore")
|
||||
try:
|
||||
public.ExecShell("rm -rf {}/{}_backup".format(self.base_path, timestamp))
|
||||
except:
|
||||
pass
|
||||
|
||||
if not os.path.exists(self.base_path + "/{timestamp}_backup".format(timestamp=timestamp)):
|
||||
public.ExecShell("cd {}/ && tar -xvf {}".format(self.base_path, backup_file))
|
||||
|
||||
restore_data_path = self.base_path + "/{timestamp}_backup".format(timestamp=timestamp)
|
||||
public.ExecShell("\cp -rpa {}/backup.json {}/restore.json".format(restore_data_path, restore_data_path))
|
||||
|
||||
restore_info = self.get_restore_data_list(timestamp)
|
||||
restore_info["force_restore"] = self.overwrite # 覆盖标志
|
||||
restore_info['restore_status'] = 1 # 更新状态
|
||||
self.update_restore_data_list(timestamp, restore_info)
|
||||
|
||||
start_time = int(time.time())
|
||||
# ============================= START ==================================
|
||||
self.print_log(public.lang("Start restoring data"), "restore")
|
||||
# ============================= env ====================================
|
||||
try:
|
||||
self.restore_env(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log(f"restore env error: {str(e)}")
|
||||
|
||||
# ============================= site ====================================
|
||||
try:
|
||||
self.restore_site_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore site error: {}".format(str(e)))
|
||||
finally:
|
||||
self.chmod_dir_file("/www/wwwroot", dir_mode=0o755, file_mode=0o644)
|
||||
|
||||
# ============================= ftp =====================================
|
||||
try:
|
||||
self.restore_ftp_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore ftp error: {}".format(str(e)))
|
||||
|
||||
# ============================= database =================================
|
||||
try:
|
||||
self.restore_database_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore database error: {}".format(str(e)))
|
||||
finally:
|
||||
if not self.overwrite:
|
||||
try: # 补全关系
|
||||
self.fix_wp_onekey(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("fix forign key error: {}".format(str(e)))
|
||||
|
||||
# ============================= ssl ======================================
|
||||
try:
|
||||
self.restore_ssl_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore ssl error: {}".format(str(e)))
|
||||
|
||||
# ============================= cron task ================================
|
||||
try:
|
||||
self.restore_crontab_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore cron task error: {}".format(str(e)))
|
||||
finally:
|
||||
self.reload_crontab()
|
||||
|
||||
# ============================== ssh ======================================
|
||||
# TDDO: 存在问题,下个版本修复
|
||||
# try:
|
||||
# SshModule().restore_ssh_data(timestamp)
|
||||
# except Exception as e:
|
||||
# public.print_log("restore ssh error: {}".format(str(e)))
|
||||
|
||||
# ============================= firewall ==================================
|
||||
try:
|
||||
self.restore_firewall_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore firewall error: {}".format(str(e)))
|
||||
|
||||
# ============================= mail ======================================
|
||||
try:
|
||||
self.restore_vmail_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore mail error: {}".format(str(e)))
|
||||
|
||||
# ============================= plugin ====================================
|
||||
try:
|
||||
self.restore_plugin_data(timestamp)
|
||||
except Exception as e:
|
||||
public.print_log("restore plugin error: {}".format(str(e)))
|
||||
|
||||
# ============================= END =======================================
|
||||
|
||||
end_time = int(time.time())
|
||||
done_time = datetime.datetime.fromtimestamp(int(end_time)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
total_time = end_time - start_time
|
||||
backup_conf = self.get_backup_conf(timestamp)
|
||||
backup_conf['restore_status'] = 2
|
||||
backup_conf['restore_done_time'] = done_time
|
||||
backup_conf['restore_total_time'] = total_time
|
||||
self.save_backup_conf(timestamp, backup_conf)
|
||||
|
||||
restore_info['restore_status'] = 2
|
||||
restore_info['restore_done_time'] = done_time
|
||||
restore_info['restore_total_time'] = total_time
|
||||
self.update_restore_data_list(timestamp, restore_info)
|
||||
|
||||
self.print_log("==================================", "restore")
|
||||
self.print_log(public.lang("Data restoration completed"), "restore")
|
||||
|
||||
public.WriteFile(self.restore_success_file, timestamp)
|
||||
public.ExecShell("rm -f {}".format(self.restore_pl_file))
|
||||
if not os.path.exists(self.history_log_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_log_path))
|
||||
if not os.path.exists(self.history_info_path):
|
||||
public.ExecShell("mkdir -p {}".format(self.history_info_path))
|
||||
|
||||
hitory_log_file = self.history_log_path + '/' + str(timestamp) + '_restore.log'
|
||||
history_info_file = self.history_info_path + '/' + str(timestamp) + '_restore.info'
|
||||
public.WriteFile(
|
||||
hitory_log_file,
|
||||
public.ReadFile("/www/backup/backup_restore/restore.log".format(timestamp))
|
||||
)
|
||||
public.WriteFile(
|
||||
history_info_file,
|
||||
public.ReadFile("/www/backup/backup_restore/{}_backup/restore.json".format(timestamp))
|
||||
)
|
||||
if os.path.exists("/www/server/panel/data/migration.pl"):
|
||||
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
|
||||
|
||||
# 重启服务
|
||||
public.ServiceReload()
|
||||
time.sleep(2)
|
||||
public.ExecShell("/etc/init.d/bt restart")
|
||||
except Exception as e:
|
||||
return public.returnMsg(False, public.lang("Data restoration failed: {}").format(str(e)))
|
||||
finally:
|
||||
if os.path.exists(self.restore_pl_file):
|
||||
public.ExecShell("rm -f {}".format(self.restore_pl_file))
|
||||
|
||||
def get_restore_log(self, timestamp):
|
||||
restore_log_file = self.base_path + '/restore.log'
|
||||
history_log_file = self.history_log_path + '/' + str(timestamp) + '_restore.log'
|
||||
if os.path.exists(self.restore_pl_file):
|
||||
restore_timestamp = int(public.ReadFile(self.restore_pl_file))
|
||||
if int(restore_timestamp) == int(timestamp):
|
||||
return public.ReadFile(restore_log_file)
|
||||
if os.path.exists(history_log_file):
|
||||
return public.ReadFile(history_log_file)
|
||||
else:
|
||||
return ""
|
||||
|
||||
def get_restore_details(self, timestamp):
|
||||
history_info_file = self.history_info_path + '/' + str(timestamp) + '_restore.info'
|
||||
if not os.path.exists(history_info_file):
|
||||
return public.fail_v2(public.lang("File does not exist"))
|
||||
|
||||
restore_info = json.loads(public.ReadFile(history_info_file))
|
||||
restore_info = self.process_detail(restore_info)
|
||||
|
||||
restore_task_info = self.get_backup_conf(timestamp)
|
||||
restore_info['backup_file_size'] = restore_task_info['backup_file_size']
|
||||
restore_info['backup_file_sha256'] = restore_task_info['backup_file_sha256']
|
||||
restore_info['create_time'] = restore_task_info['create_time']
|
||||
restore_info['backup_time'] = restore_task_info['backup_time']
|
||||
restore_info['backup_file'] = restore_task_info['backup_file']
|
||||
restore_info['backup_path'] = restore_task_info['backup_path']
|
||||
restore_info['restore_done_time'] = restore_task_info['restore_done_time']
|
||||
restore_info['restore_total_time'] = restore_task_info['restore_total_time']
|
||||
restore_info['type'] = "restore"
|
||||
return public.success_v2(restore_info)
|
||||
|
||||
# todo 弃用
|
||||
def get_restore_progress(self, get=None):
|
||||
"""
|
||||
获取还原进度信息
|
||||
@param get: object 包含请求参数
|
||||
@return: dict 还原进度信息
|
||||
"""
|
||||
# 设置相关文件路径
|
||||
restore_pl_file = self.base_path + '/restore.pl'
|
||||
restore_log_file = self.base_path + '/restore.log'
|
||||
restore_success_file = self.base_path + '/restore_success.pl'
|
||||
|
||||
# 创建处理已完成备份的函数,减少代码重复
|
||||
def create_completed_result(restore_timestamp):
|
||||
if not restore_timestamp:
|
||||
return public.ReturnMsg(False, public.lang("Restore completed but unable to get restore timestamp"))
|
||||
|
||||
if not os.path.exists(self.bakcup_task_json):
|
||||
return public.ReturnMsg(False, public.lang("Restore configuration file does not exist"))
|
||||
|
||||
restore_configs = json.loads(public.ReadFile(self.bakcup_task_json))
|
||||
success_data = next(
|
||||
(item for item in restore_configs if str(item.get('timestamp')) == str(restore_timestamp)), {})
|
||||
|
||||
return {
|
||||
"task_type": "restore",
|
||||
"task_status": 2,
|
||||
"backup_data": None,
|
||||
"backup_name": None,
|
||||
"data_backup_status": 2,
|
||||
"progress": 100,
|
||||
"msg": None,
|
||||
'exec_log': public.ReadFile(restore_log_file) if os.path.exists(restore_log_file) else "",
|
||||
'timestamp': restore_timestamp,
|
||||
'backup_file_info': success_data
|
||||
}
|
||||
|
||||
# 检查备份是否已完成
|
||||
if os.path.exists(restore_success_file):
|
||||
success_time = int(os.path.getctime(restore_success_file))
|
||||
local_time = int(time.time())
|
||||
# 如果success文件创建时间在10秒内,说明备份刚刚完成
|
||||
if success_time + 10 > local_time:
|
||||
try:
|
||||
restore_timestamp = public.ReadFile(restore_success_file).strip()
|
||||
return public.ReturnMsg(True, create_completed_result(restore_timestamp))
|
||||
except Exception as e:
|
||||
public.ExecShell("rm -f {}".format(restore_success_file))
|
||||
return public.ReturnMsg(False,
|
||||
public.lang("Error getting restore completion information: {}").format(
|
||||
str(e)))
|
||||
else:
|
||||
# 超过10秒,删除success文件
|
||||
public.ExecShell("rm -f {}".format(restore_success_file))
|
||||
|
||||
# 检查是否有备份进程运行
|
||||
try:
|
||||
# 检查备份进程锁文件
|
||||
if os.path.exists(restore_pl_file):
|
||||
timestamp = public.ReadFile(restore_pl_file).strip()
|
||||
if not timestamp:
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"Restore process is running, but unable to get restore timestamp"))
|
||||
else:
|
||||
# 等待2秒,可能是还原刚刚完成
|
||||
time.sleep(2)
|
||||
if os.path.exists(restore_success_file):
|
||||
success_time = int(os.path.getctime(restore_success_file))
|
||||
local_time = int(time.time())
|
||||
if success_time + 10 > local_time:
|
||||
restore_timestamp = public.ReadFile(restore_success_file).strip()
|
||||
return public.ReturnMsg(True, create_completed_result(restore_timestamp))
|
||||
|
||||
# 再次检查是否有备份进程
|
||||
if os.path.exists(restore_success_file):
|
||||
timestamp = public.ReadFile(restore_success_file).strip()
|
||||
if not timestamp:
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"Restore process is running, but unable to get restore timestamp"))
|
||||
else:
|
||||
return public.ReturnMsg(False, public.lang(
|
||||
"No restore task found, please check the restore list to see if the restore is complete"))
|
||||
|
||||
# 读取备份配置文件
|
||||
restore_json_path = f"{self.base_path}/{timestamp}_backup/restore.json"
|
||||
count = 0
|
||||
while 1:
|
||||
if count >= 3:
|
||||
return public.ReturnMsg(False, public.lang("Restore configuration file does not exist: {}").format(
|
||||
restore_json_path))
|
||||
count += 1
|
||||
if not os.path.exists(restore_json_path):
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
|
||||
conf_data = json.loads(public.ReadFile(restore_json_path))
|
||||
except Exception as e:
|
||||
return public.ReturnMsg(False, public.lang("Error getting restore progress information: {}").format(str(e)))
|
||||
|
||||
# 读取备份日志
|
||||
restore_log_data = public.ReadFile(restore_log_file) if os.path.exists(restore_log_file) else ""
|
||||
|
||||
# 定义备份类型及其处理逻辑
|
||||
restore_types = [
|
||||
{
|
||||
'type': 'site',
|
||||
'data_key': 'site',
|
||||
'display_name': 'site',
|
||||
'progress': 30
|
||||
},
|
||||
{
|
||||
'type': 'database',
|
||||
'data_key': 'database',
|
||||
'display_name': 'database',
|
||||
'progress': 60
|
||||
},
|
||||
{
|
||||
'type': 'ftp',
|
||||
'data_key': 'ftp',
|
||||
'display_name': 'ftp',
|
||||
'progress': 70
|
||||
},
|
||||
{
|
||||
'type': 'ssh',
|
||||
'data_key': 'ssh',
|
||||
'display_name': 'ssh',
|
||||
'progress': 75
|
||||
},
|
||||
{
|
||||
'type': 'firewall',
|
||||
'data_key': 'firewall',
|
||||
'display_name': 'firewall',
|
||||
'progress': 90
|
||||
}
|
||||
]
|
||||
|
||||
# 检查软件环境状态
|
||||
if "data_list" in conf_data and "soft" in conf_data["data_list"]:
|
||||
soft_data = conf_data["data_list"]["soft"]
|
||||
if "status" in soft_data and soft_data["status"].get("status") == 1:
|
||||
name = soft_data["status"].get("name", "unknown")
|
||||
# version = soft_data["status"].get("version", "unknown")
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "restore",
|
||||
"task_status": 1,
|
||||
"data_type": "soft",
|
||||
"name": name,
|
||||
"data_backup_status": 1,
|
||||
"progress": 20,
|
||||
"msg": soft_data["status"].get("err_msg"),
|
||||
'exec_log': restore_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
|
||||
# 检查各类型备份进度
|
||||
for restore_type in restore_types:
|
||||
items = conf_data.get("data_list", {}).get(restore_type['data_key'], [])
|
||||
for item in items:
|
||||
try:
|
||||
if item.get("restore_status") == 2:
|
||||
continue
|
||||
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "restore",
|
||||
"task_status": 1,
|
||||
"data_type": restore_type['type'],
|
||||
"name": item.get("name", public.lang("Unknown {}").format(restore_type['display_name'])),
|
||||
"data_backup_status": item.get("status", 0),
|
||||
"progress": restore_type['progress'],
|
||||
"msg": item.get("msg"),
|
||||
'exec_log': restore_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
except:
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "restore",
|
||||
"task_status": 1,
|
||||
"data_type": public.lang("Server Configuration"),
|
||||
"name": public.lang("Server Configuration"),
|
||||
"data_backup_status": 2,
|
||||
"progress": 90,
|
||||
"msg": None,
|
||||
'exec_log': restore_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
|
||||
# 检查数据打包进度
|
||||
try:
|
||||
restore_status = conf_data.get('restore_status')
|
||||
if restore_status == 1:
|
||||
return public.ReturnMsg(True, {
|
||||
"task_type": "restore",
|
||||
"task_status": 1,
|
||||
"data_type": "tar",
|
||||
"name": public.lang("Data Decompression"),
|
||||
"data_backup_status": 1,
|
||||
"progress": 10,
|
||||
'exec_log': restore_log_data,
|
||||
'timestamp': timestamp
|
||||
})
|
||||
except Exception:
|
||||
# 可能没有backup_status字段,继续处理
|
||||
pass
|
||||
|
||||
# 如果没有发现进行中的任务,但有备份进程
|
||||
if timestamp:
|
||||
return {
|
||||
"backup_data": "unknown",
|
||||
"backup_name": public.lang("Unknown Task"),
|
||||
"data_backup_status": 1,
|
||||
"progress": 10,
|
||||
'backup_msg': public.lang("Preparing to restore data"),
|
||||
'backup_log': restore_log_data,
|
||||
'timestamp': timestamp
|
||||
}
|
||||
|
||||
return public.ReturnMsg(
|
||||
False,
|
||||
public.lang(
|
||||
"No ongoing restore task found, please check the restore list to see if the restore is complete"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取命令行参数
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: btpython restore_manager.py <method> <timestamp>")
|
||||
sys.exit(1)
|
||||
method_name = sys.argv[1] # 方法名
|
||||
timestamp = sys.argv[2] # 时间戳
|
||||
overwrite = sys.argv[3] if len(sys.argv) >= 3 else 0
|
||||
try:
|
||||
overwrite = int(overwrite)
|
||||
except ValueError:
|
||||
print(public.lang("Error: force parameter must be 0 or 1"))
|
||||
sys.exit(1)
|
||||
restore_manager = RestoreManager(overwrite) # 实例化对象
|
||||
if hasattr(restore_manager, method_name): # 检查方法是否存在
|
||||
method = getattr(restore_manager, method_name) # 获取方法
|
||||
method(timestamp) # 调用方法
|
||||
else:
|
||||
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")
|
||||
1751
mod/project/backup_restore/ssh_manager.py
Normal file
1751
mod/project/backup_restore/ssh_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user