mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-09-05 09:17:38 +02:00
771 lines
35 KiB
Python
771 lines
35 KiB
Python
# coding: utf-8
|
|
# -------------------------------------------------------------------
|
|
# aapanel
|
|
# -------------------------------------------------------------------
|
|
# Copyright (c) 2015-2099 aapanel(http://www.aapanel.com) All rights reserved.
|
|
# -------------------------------------------------------------------
|
|
# Author: miku <wzz@bt.cn>
|
|
# -------------------------------------------------------------------
|
|
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
|
|
try:
|
|
from mod.project.backup_restore.modules.node_module import NodeModule
|
|
except:
|
|
NodeModule = None
|
|
|
|
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 not os.path.exists(self.base_path):
|
|
os.makedirs(self.base_path, exist_ok=True)
|
|
|
|
if os.path.exists(self.bakcup_task_json):
|
|
# 修复 JSON解析崩溃
|
|
content = public.ReadFile(self.bakcup_task_json)
|
|
if content and content.strip():
|
|
try:
|
|
backup_list = json.loads(content)
|
|
except:
|
|
backup_list = []
|
|
|
|
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))
|
|
|
|
for backup_conf in backup_list:
|
|
if not isinstance(backup_conf, dict) or str(backup_conf.get("backup_status")) != "2":
|
|
continue
|
|
timestamp = backup_conf.get("timestamp")
|
|
if not timestamp:
|
|
continue
|
|
backup_json_path = os.path.join(self.base_path, "{}_backup".format(timestamp), "backup.json")
|
|
history_info_file = os.path.join(self.history_info_path, "{}_backup.info".format(timestamp))
|
|
for info_file in (backup_json_path, history_info_file):
|
|
if not os.path.exists(info_file):
|
|
continue
|
|
try:
|
|
backup_info = json.loads(public.ReadFile(info_file) or "{}")
|
|
data_list = backup_info.get("data_list", {})
|
|
if not isinstance(data_list, dict):
|
|
continue
|
|
backup_count = {
|
|
"success": self.count_backup_status(data_list, 2),
|
|
"failed": self.count_backup_status(data_list, 3),
|
|
}
|
|
backup_conf["backup_count"] = backup_count
|
|
if backup_info.get("backup_count") != backup_count:
|
|
backup_info["backup_count"] = backup_count
|
|
public.WriteFile(info_file, json.dumps(backup_info))
|
|
break
|
|
except:
|
|
continue
|
|
|
|
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_data_list = self.normalize_backup_data(backup_conf.get("backup_data"))
|
|
backup_conf['backup_data'] = backup_data_list
|
|
backup_conf['site_id'] = self.normalize_backup_id_list(backup_conf.get("site_id"))
|
|
wp_site_id_value = backup_conf.get("wp_site_id")
|
|
if wp_site_id_value is None:
|
|
wp_site_id_value = backup_conf.get("wp_id")
|
|
backup_conf['wp_site_id'] = self.normalize_backup_id_list(wp_site_id_value)
|
|
backup_conf.pop("wp_id", None)
|
|
backup_conf['database_id'] = self.normalize_backup_id_list(backup_conf.get("database_id"))
|
|
backup_conf['data_list'] = {}
|
|
if self.backup_type_enabled(backup_data_list, "soft"):
|
|
backup_conf['data_list']['soft'] = self.get_soft_data(timestamp, packet=True)
|
|
else:
|
|
backup_conf['data_list']['soft'] = {}
|
|
|
|
if self.backup_type_enabled(backup_data_list, "site", "wp_tools"):
|
|
backup_conf['data_list']['site'] = self.get_site_backup_conf(timestamp)
|
|
else:
|
|
backup_conf['data_list']['site'] = []
|
|
|
|
if self.backup_type_enabled(backup_data_list, "ssl"):
|
|
backup_conf['data_list']['ssl'] = self.get_ssl_backup_conf(timestamp)
|
|
else:
|
|
backup_conf['data_list']['ssl'] = {"ssl_list": [], "provider_list": []}
|
|
|
|
if self.backup_type_enabled(backup_data_list, "database"):
|
|
backup_conf['data_list']['database'] = self.get_database_backup_conf(timestamp)
|
|
else:
|
|
backup_conf['data_list']['database'] = []
|
|
|
|
if self.backup_type_enabled(backup_data_list, "ftp"):
|
|
backup_conf['data_list']['ftp'] = self.backup_ftp_data(timestamp)
|
|
else:
|
|
backup_conf['data_list']['ftp'] = []
|
|
|
|
backup_conf['backup_status'] = 1
|
|
self.save_backup_conf(str(timestamp), backup_conf)
|
|
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)
|
|
self.write_initial_task_state("backup", timestamp)
|
|
|
|
backup_conf = self.get_backup_conf(timestamp)
|
|
if not backup_conf:
|
|
return public.returnMsg(False, public.lang("Backup configuration file does not exist"))
|
|
backup_data_list = self.normalize_backup_data(backup_conf.get("backup_data"))
|
|
backup_conf['backup_data'] = backup_data_list
|
|
backup_conf['site_id'] = self.normalize_backup_id_list(backup_conf.get("site_id"))
|
|
wp_site_id_value = backup_conf.get("wp_site_id")
|
|
if wp_site_id_value is None:
|
|
wp_site_id_value = backup_conf.get("wp_id")
|
|
backup_conf['wp_site_id'] = self.normalize_backup_id_list(wp_site_id_value)
|
|
backup_conf.pop("wp_id", None)
|
|
backup_conf['database_id'] = self.normalize_backup_id_list(backup_conf.get("database_id"))
|
|
backup_conf['backup_status'] = 1
|
|
self.save_backup_conf(timestamp, backup_conf)
|
|
start_time = int(time.time())
|
|
# 构造备份初始配置
|
|
self.add_backup_task(timestamp)
|
|
self.clear_initial_task_state("backup")
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "site", "wp_tools"):
|
|
self.backup_site_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "database"):
|
|
self.backup_database_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "ssl"):
|
|
self.backup_ssl_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "crontab"):
|
|
CrontabModule().backup_crontab_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
# TODO: 存在问题,下个版本修复
|
|
# try:
|
|
# SshModule().backup_ssh_data(timestamp)
|
|
# except:
|
|
# pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "ssh"):
|
|
SshModule().backup_ssh_data(timestamp)
|
|
except Exception as e:
|
|
public.print_log("backup ssh error: {}".format(str(e)))
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "firewall"):
|
|
FirewallModule().backup_firewall_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "vmail"):
|
|
MailModule().backup_vmail_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if self.backup_type_enabled(backup_data_list, "plugin"):
|
|
PluginModule().backup_plugin_data(timestamp)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if NodeModule is not None and self.backup_type_enabled(backup_data_list, "node"):
|
|
NodeModule().backup_node_data(timestamp)
|
|
except Exception as e:
|
|
public.print_log("backup node error: {}".format(str(e)))
|
|
|
|
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:
|
|
self.clear_initial_task_state("backup")
|
|
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):
|
|
def json_count(file_path):
|
|
try:
|
|
content = public.ReadFile(file_path).strip()
|
|
value = json.loads(content)
|
|
return len(value) if isinstance(value, (list, dict)) else 0
|
|
except:
|
|
return 0
|
|
|
|
def status_count(value):
|
|
if isinstance(value, list):
|
|
return sum(status_count(item) for item in value)
|
|
if not isinstance(value, dict):
|
|
return 0
|
|
if "status" in value:
|
|
return 1 if value.get("status") == status_code else 0
|
|
return sum(status_count(item) for item in value.values())
|
|
|
|
count = 0
|
|
for category_key, category_data in data.items():
|
|
if category_key == "crontab" and isinstance(category_data, dict):
|
|
if category_data.get("status") == status_code:
|
|
count += json_count(category_data.get("crontab_json"))
|
|
elif category_key == "firewall" and isinstance(category_data, dict):
|
|
if category_data.get("status") == status_code:
|
|
count += 4
|
|
elif category_key == "vmail" and isinstance(category_data, dict):
|
|
if category_data.get("status") == status_code and category_data.get("vmail_backup_name"):
|
|
count += 1
|
|
else:
|
|
count += status_count(category_data)
|
|
return count
|
|
|
|
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
|
|
conf_data["backup_count"] = {
|
|
"success": status_2_count,
|
|
"failed": status_3_count,
|
|
}
|
|
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"
|
|
backup_conf["cloud_upload_status"] = 1
|
|
backup_conf["cloud_storage"] = storage_type
|
|
# public.print_log("[backup_restore] cloud upload start: timestamp={}, storage_type={}, file={}".format(timestamp, storage_type, tar_file_name))
|
|
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))
|
|
backup_conf["cloud_name"] = cloud_name_cn
|
|
backup_conf["cloud_upload_path"] = upload_path
|
|
if _cloud.cloud_upload_file(tar_file_name, upload_path):
|
|
backup_conf["cloud_upload_status"] = 2
|
|
# public.print_log("[backup_restore] cloud upload success: timestamp={}, storage_type={}, path={}".format(timestamp, storage_type, upload_path))
|
|
self.print_log(public.lang("Successfully uploaded to {}").format(cloud_name_cn), "backup")
|
|
else:
|
|
backup_conf["cloud_upload_status"] = 3
|
|
# public.print_log("[backup_restore] cloud upload failed: timestamp={}, storage_type={}, path={}".format(timestamp, storage_type, upload_path))
|
|
self.print_log(public.lang("Failed to upload to {}").format(cloud_name_cn), "backup")
|
|
except Exception as e:
|
|
import traceback
|
|
backup_conf["cloud_upload_status"] = 3
|
|
backup_conf["cloud_name"] = cloud_name_cn
|
|
# public.print_log("[backup_restore] cloud upload exception: timestamp={}, storage_type={}, error={}".format(timestamp, storage_type, str(e)))
|
|
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': []
|
|
}
|
|
|
|
def create_initial_result(backup_timestamp=None):
|
|
backup_log_data = public.ReadFile(backup_log_file) if os.path.exists(backup_log_file) else ""
|
|
return self.build_initial_task_progress("backup", backup_timestamp, backup_log_data)
|
|
|
|
# 检查备份是否已完成
|
|
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(True, create_initial_result())
|
|
else:
|
|
init_state = self.get_initial_task_state("backup")
|
|
if init_state:
|
|
return public.ReturnMsg(True, create_initial_result(init_state.get("timestamp")))
|
|
|
|
# 等待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(True, create_initial_result())
|
|
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(True, create_initial_result(timestamp))
|
|
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")
|