561 lines
21 KiB
Python
561 lines
21 KiB
Python
import json
|
||
import os.path
|
||
import threading
|
||
import traceback
|
||
import public
|
||
from mod.base import json_response
|
||
from mod.base.ssh_executor import test_ssh_config
|
||
try:
|
||
from mod.project.node.nodeutil import ServerNode, LocalNode, monitor_node_once_with_timeout
|
||
except:
|
||
# 定义处理h11的命令变量
|
||
cmd_h11 = "cd /www/server/panel/pyenv/bin && source activate && H11_VERSION=$(./pip3 show h11 | grep -i Version | awk '{print $2}') && if [ \"$H11_VERSION\" != \"0.14.0\" ]; then ./pip3 uninstall h11 -y; fi; ./pip3 install h11==0.14.0"
|
||
|
||
# 定义处理wsproto的命令变量
|
||
cmd_wsproto = "cd /www/server/panel/pyenv/bin && source activate && WSPROTO_VERSION=$(./pip3 show wsproto | grep -i Version | awk '{print $2}') && if [ \"$WSPROTO_VERSION\" != \"1.2.0\" ]; then ./pip3 uninstall wsproto -y; fi; ./pip3 install wsproto==1.2.0"
|
||
public.ExecShell(cmd_h11)
|
||
public.ExecShell(cmd_wsproto)
|
||
from mod.project.node.nodeutil import ServerNode, LocalNode, monitor_node_once_with_timeout
|
||
from mod.project.node.dbutil import Node, ServerNodeDB, ServerMonitorRepo
|
||
from mod.project.node.task_flow import flow_useful_version
|
||
|
||
|
||
class main():
|
||
node_db_obj = ServerNodeDB()
|
||
node_db_file =node_db_obj._DB_FILE
|
||
def __init__(self):
|
||
# self.node_db_obj = ServerNodeDB()
|
||
self.tip_file = public.get_panel_path() + "/data/mod_node_used.pl"
|
||
self.show_mode_file = public.get_panel_path() + "/data/mod_node_show_mode.pl"
|
||
|
||
def add_node(self, get):
|
||
"""
|
||
增加节点
|
||
:param get: address节点地址 api_key节点API Key remarks节点备注 category_id节点分类ID
|
||
:return:
|
||
"""
|
||
ssh_conf = get.get('ssh_conf', "{}")
|
||
try:
|
||
get.ssh_conf = json.loads(ssh_conf)
|
||
except Exception:
|
||
return public.return_message(-1, 0,"SSH_conf data format error")
|
||
n, err = Node.from_dict(get)
|
||
if not n:
|
||
return public.return_message(-1, 0, err)
|
||
public.set_module_logs("nodes_node_adds_9", "add_node")
|
||
if n.app_key or n.api_key:
|
||
err = ServerNode.check_api_key(n)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
else:
|
||
# ssh 节点,不用处理
|
||
pass
|
||
|
||
n.server_ip = n.parse_server_ip()
|
||
err = ServerNodeDB().create_node(n)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
node = ServerNodeDB().get_node_by_id(n.id)
|
||
if node:
|
||
monitor_node_once_with_timeout(node)
|
||
return public.return_message(0, 0, "Node added successfully")
|
||
|
||
@staticmethod
|
||
def bind_app(get):
|
||
n, err = Node.from_dict(get)
|
||
if not n:
|
||
return public.return_message(-1, 0, err)
|
||
if not n.app_key:
|
||
return public.return_message(-1, 0, "Please specify the app key to bind to")
|
||
srv = ServerNode("", "", n.app_key)
|
||
res = srv.app_bind()
|
||
if res:
|
||
return public.return_message(-1, 0, res)
|
||
else:
|
||
return public.return_message(0, 0, "Binding request has been sent out")
|
||
|
||
@staticmethod
|
||
def bind_app_status(get):
|
||
n, err = Node.from_dict(get)
|
||
if not n:
|
||
return public.return_message(-1, 0, err)
|
||
if not n.app_key:
|
||
return public.return_message(-1, 0, "Please specify the app key to bind to")
|
||
srv = ServerNode("", "", n.app_key)
|
||
res = srv.app_bind_status()
|
||
if res:
|
||
return public.return_message(-1, 0, res)
|
||
else:
|
||
return public.return_message(0, 0, "Binding successful")
|
||
|
||
|
||
def del_node(self, get):
|
||
"""
|
||
删除节点
|
||
:param get: ids节点ID
|
||
:return:
|
||
"""
|
||
node_ids = get.get('ids', "")
|
||
if not node_ids:
|
||
return public.return_message(-1, 0, "Node ID cannot be empty, at least one")
|
||
try:
|
||
node_ids = json.loads(node_ids)
|
||
if not isinstance(node_ids, list) and isinstance(node_ids, int):
|
||
node_ids = [node_ids]
|
||
except Exception:
|
||
return public.return_message(-1, 0, "The format of the node ID data passed in is incorrect")
|
||
|
||
srv_db = ServerNodeDB()
|
||
for node_id in node_ids:
|
||
if srv_db.is_local_node(node_id):
|
||
continue
|
||
err = srv_db.delete_node(node_id)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
return public.return_message(0, 0, "Node deleted successfully")
|
||
|
||
def update_node(self, get):
|
||
"""
|
||
更新节点
|
||
:param get: id节点ID address节点地址 api_key节点API Key remarks节点备注 category_id节点分类ID
|
||
:return:
|
||
"""
|
||
node_id = get.get('id/d', 0)
|
||
ssh_conf = get.get('ssh_conf', "{}")
|
||
try:
|
||
get.ssh_conf = json.loads(ssh_conf)
|
||
except Exception:
|
||
return public.return_message(-1, 0, "SSH_conf data format error")
|
||
if not node_id:
|
||
return public.return_message(-1, 0, "Node ID cannot be empty")
|
||
n, err = Node.from_dict(get)
|
||
if not n:
|
||
return public.return_message(-1, 0, err)
|
||
if n.app_key or n.api_key:
|
||
err = ServerNode.check_api_key(n)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
|
||
n.server_ip = n.parse_server_ip()
|
||
srv_db = ServerNodeDB()
|
||
err = srv_db.update_node(n, with_out_fields=["id", "status", "error", "error_num"])
|
||
if err:
|
||
return public.return_message(-1, 0,err)
|
||
node = ServerNodeDB().get_node_by_id(n.id)
|
||
if node:
|
||
monitor_node_once_with_timeout(node)
|
||
return public.return_message(0, 0, "Node update successful")
|
||
|
||
def default_show_mode(self) -> str:
|
||
if not os.path.exists(self.show_mode_file):
|
||
return "list"
|
||
show_mode = public.readFile(self.show_mode_file)
|
||
if not show_mode:
|
||
return "list"
|
||
if show_mode not in ["list", "block"]:
|
||
return "list"
|
||
return show_mode
|
||
|
||
def set_show_mode(self, mode_name: str):
|
||
if mode_name not in ["list", "block"]:
|
||
return False
|
||
if mode_name == "block":
|
||
public.set_module_logs("node_show_block", "node_show_block")
|
||
public.writeFile(self.show_mode_file, mode_name)
|
||
return True
|
||
|
||
def get_node_list(self, get):
|
||
"""
|
||
获取节点列表
|
||
:param get: p页码 limit每页数量 search搜索关键字 category_id分类ID
|
||
:return:
|
||
"""
|
||
page_num = max(int(get.get('p/d', 1)), 1)
|
||
limit = max(int(get.get('limit/d', 10)), 10)
|
||
search = get.get('search', "").strip()
|
||
category_id = get.get('category_id/d', -1)
|
||
refresh = get.get('refresh/s', "")
|
||
show_mode = get.get('show_mode/s', "")
|
||
if not show_mode or show_mode not in ["list", "block"]:
|
||
show_mode = self.default_show_mode()
|
||
else:
|
||
if not self.set_show_mode(show_mode):
|
||
show_mode = self.default_show_mode()
|
||
|
||
if show_mode == "block": # 返回所有数据
|
||
page_num = 1
|
||
limit = 9999999
|
||
|
||
srv_db = ServerNodeDB()
|
||
data, err = srv_db.get_node_list(search, category_id, (page_num - 1) * limit, limit)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
|
||
if refresh and refresh == "1":
|
||
th_list = []
|
||
for node in data:
|
||
th = threading.Thread(target=monitor_node_once_with_timeout, args=(node,5))
|
||
th.start()
|
||
th_list.append(th)
|
||
|
||
for th in th_list:
|
||
th.join()
|
||
|
||
for node in data:
|
||
if isinstance(node["ssh_conf"], str):
|
||
node["ssh_conf"] = json.loads(node["ssh_conf"])
|
||
if isinstance(node["error"], str):
|
||
node["error"] = json.loads(node["error"])
|
||
if node["app_key"] == "local" and node["api_key"] == "local":
|
||
node["address"] = public.getPanelAddr()
|
||
if node["lpver"] and not node["remarks"].endswith(" | 1Panel"):
|
||
node["remarks"] = node["remarks"] + " | 1Panel"
|
||
node_data = self.get_node_data(node)
|
||
node['data'] = node_data
|
||
count = srv_db.node_count(search, category_id)
|
||
page = public.get_page(count, page_num, limit)
|
||
page["data"] = data
|
||
page["show_mode"] = show_mode
|
||
return public.return_message(0, 0,page)
|
||
|
||
@staticmethod
|
||
def get_node_data(node: dict):
|
||
if node["app_key"] == "local" and node["api_key"] == "local":
|
||
data = ServerMonitorRepo.get_local_server_status()
|
||
else:
|
||
srv_m = ServerMonitorRepo()
|
||
if srv_m.is_reboot_wait(node["server_ip"]):
|
||
return {'status': 4, 'msg': "Server restart in progress..."}
|
||
# public.print_log("get_node_data-------------------------1:{}".format(node["id"]))
|
||
data = srv_m.get_server_status(node['id'])
|
||
# public.print_log("get_node_data------------data----------2---:{}".format(data))
|
||
if data:
|
||
cpu_data = data.get('cpu', {})
|
||
memory_data = data.get('mem', {})
|
||
if cpu_data and memory_data:
|
||
return {
|
||
'status': 0,
|
||
'cpu': cpu_data[0],
|
||
'cpu_usage': cpu_data[1],
|
||
'memory': round(float(memory_data['memRealUsed']) / float(memory_data['memTotal']) * 100, 2),
|
||
'mem_usage': memory_data['memRealUsed'],
|
||
'memNewTotal': memory_data.get('memNewTotal', "") or public.to_size(
|
||
memory_data['memTotal'] * 1024 * 1024)
|
||
}
|
||
return {'status': 2, 'msg': "Failed to obtain node data"}
|
||
|
||
def add_category(self, get):
|
||
"""
|
||
添加分类
|
||
:param get:
|
||
:return:
|
||
"""
|
||
name = get.get('name', "").strip()
|
||
srv_db = ServerNodeDB()
|
||
if not name:
|
||
return public.return_message(-1, 0, "Classification name cannot be empty")
|
||
if srv_db.category_exites(name):
|
||
return public.return_message(-1, 0, "The category name already exists")
|
||
err = srv_db.create_category(name)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
return public.return_message(0, 0, "Category added successfully")
|
||
|
||
def del_category(self, get):
|
||
"""
|
||
删除分类
|
||
:param get:
|
||
:return:
|
||
"""
|
||
category_id = get.get('id/d', 0)
|
||
if not category_id:
|
||
return public.return_message(-1, 0, "Classification ID cannot be empty")
|
||
srv_db = ServerNodeDB()
|
||
if srv_db.category_exites(category_id):
|
||
srv_db.delete_category(category_id)
|
||
|
||
return public.return_message(0, 0, "Category deleted successfully")
|
||
|
||
def bind_node_to_category(self, get):
|
||
"""
|
||
绑定节点到分类 可以批量绑定
|
||
:param get: 如果传入单个node_id则是绑定单个,如果是传入列表则批量绑定
|
||
:return:
|
||
"""
|
||
node_ids = get.get('ids', "")
|
||
category_id = get.get('category_id/d', 0)
|
||
try:
|
||
node_ids = json.loads(node_ids)
|
||
if not isinstance(node_ids, list) and isinstance(node_ids, int):
|
||
node_ids = [node_ids]
|
||
except Exception:
|
||
return public.return_message(-1, 0, "Node ID format error")
|
||
|
||
if not node_ids:
|
||
return public.return_message(-1, 0, "Node ID cannot be empty, at least one")
|
||
|
||
if category_id < 0:
|
||
return public.return_message(-1, 0, "Classification ID cannot be empty")
|
||
|
||
srv_db = ServerNodeDB()
|
||
err = srv_db.bind_category_to_node(node_ids, category_id)
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
return public.return_message(0, 0, "Node grouping modification successful")
|
||
|
||
def get_category_list(self, get):
|
||
"""
|
||
获取分类列表
|
||
:param get:
|
||
:return:
|
||
"""
|
||
try:
|
||
categorys = public.S('category', self.node_db_obj._DB_FILE).select()
|
||
return public.return_message(0, 0, categorys)
|
||
except Exception:
|
||
public.print_log(traceback.print_exc())
|
||
return public.return_message(-1, 0, "Data query failed")
|
||
|
||
@staticmethod
|
||
def get_panel_url(get):
|
||
"""
|
||
获取目标面板的访问url
|
||
:param get: address节点地址 api_key节点API Key
|
||
:return:
|
||
"""
|
||
node_id = get.get('node_id/d', 0)
|
||
if not node_id:
|
||
return public.return_message(-1, 0, "node_id cannot be empty")
|
||
srv = ServerNode.new_by_id(node_id)
|
||
if not srv:
|
||
return public.return_message(-1, 0, "node_id does not exist")
|
||
token, err = srv.get_tmp_token()
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
target_panel_url = srv.origin + "/login?tmp_token=" + token
|
||
return public.return_message(0, 0, {'target_panel_url': target_panel_url})
|
||
|
||
@classmethod
|
||
def get_all_node(cls, get):
|
||
"""
|
||
@route /mod/node/node/get_all_node
|
||
@param query: str
|
||
@return: [
|
||
{
|
||
"node_id": int,
|
||
"remarks": str,
|
||
"ip": str,
|
||
}
|
||
]
|
||
"""
|
||
query_type = get.get('node_type/s', "api")
|
||
field_str = "id,remarks,server_ip,address,app_key,api_key,lpver,category_id,error_num,ssh_conf"
|
||
if query_type == "api":
|
||
data = public.S('node', cls.node_db_file).where("app_key != '' or api_key != ''", ()).field(field_str).select()
|
||
elif query_type == "ssh":
|
||
data = public.S('node', self.node_db_obj._DB_FILE).field(field_str).where("ssh_conf != '{}'", ()).select()
|
||
elif query_type == "file_src":
|
||
data = public.S('node', self.node_db_obj._DB_FILE).field(field_str).where(
|
||
"(app_key != '' or api_key != '') and lpver = ''", ()).select()
|
||
else: # all 除本机之外的节点
|
||
data = public.S('node', self.node_db_obj._DB_FILE).where("api_key != 'local'", ()).field(field_str).select()
|
||
|
||
srv_cache = ServerMonitorRepo()
|
||
for i in data:
|
||
i["has_ssh"] = bool(json.loads(i["ssh_conf"]))
|
||
i.pop("ssh_conf")
|
||
i["is_local"] = (i["app_key"] == "local" and i["api_key"] == "local")
|
||
i.pop("app_key")
|
||
i.pop("api_key")
|
||
if i["server_ip"] == "":
|
||
server_ip = ServerNode.get_node_ip(i['id'])
|
||
if server_ip:
|
||
i["server_ip"] = server_ip
|
||
if i["lpver"] and not i["remarks"].endswith(" | 1Panel"):
|
||
i["remarks"] = i["remarks"] + " | 1Panel"
|
||
|
||
tmp_data = srv_cache.get_server_status(i['id'])
|
||
tmp_data = tmp_data or {}
|
||
if query_type == "file_src":
|
||
if not tmp_data and not i["is_local"]:
|
||
i["version"] = ""
|
||
i["useful_version"] = True
|
||
continue
|
||
if not i["is_local"] or not flow_useful_version(tmp_data.get('version', "")):
|
||
continue
|
||
else:
|
||
if not tmp_data:
|
||
i["version"] = ""
|
||
i["useful_version"] = True
|
||
continue
|
||
i['version'] = tmp_data.get('version', "")
|
||
i['useful_version'] = cls._useful_version(i['version'])
|
||
|
||
return public.return_message(0, 0, data)
|
||
|
||
@staticmethod
|
||
def _useful_version(ver: str):
|
||
try:
|
||
if ver == "1Panel":
|
||
return True
|
||
ver_list = [int(i) for i in ver.split(".")]
|
||
if ver_list[0] >= 10:
|
||
return True
|
||
elif ver_list[0] == 9 and ver_list[1] >= 7:
|
||
return True
|
||
except:
|
||
pass
|
||
return False
|
||
|
||
@staticmethod
|
||
def get_node_sites(get):
|
||
"""
|
||
@route /mod/node/node/get_node_sites
|
||
@param node_id: int
|
||
@param query: str
|
||
@return: [
|
||
{
|
||
"node_id": int,
|
||
"site_id": int,
|
||
"site_name": str,
|
||
"site_port": int
|
||
}
|
||
]
|
||
"""
|
||
node_id = get.get('node_id/d', 0)
|
||
if not node_id:
|
||
return public.return_message(-1, 0, "node_id cannot be empty")
|
||
srv = ServerNode.new_by_id(node_id)
|
||
if not srv:
|
||
return public.return_message(-1, 0, "node_id does not exist")
|
||
data_list, err = srv.php_site_list()
|
||
if err:
|
||
return public.return_message(-1, 0, err)
|
||
return public.return_message(0, 0, data_list)
|
||
|
||
@staticmethod
|
||
def php_site_list(get):
|
||
"""
|
||
@route /mod/node/node/php_site_list
|
||
@return: [
|
||
{
|
||
"site_id": int,
|
||
"site_name": str,
|
||
"ports": []int,
|
||
"domains": []str,
|
||
"ssl":bool
|
||
}
|
||
]
|
||
"""
|
||
return LocalNode().php_site_list()[0]
|
||
|
||
def node_used_status(self, get):
|
||
if os.path.exists(self.tip_file):
|
||
return public.return_message(0, 0, "Used")
|
||
return public.return_message(-1, 0, "Unused")
|
||
|
||
def set_used_status(self, get):
|
||
# 定义处理h11的命令变量
|
||
cmd_h11 = "cd /www/server/panel/pyenv/bin && source activate && H11_VERSION=$(./pip3 show h11 | grep -i Version | awk '{print $2}') && if [ \"$H11_VERSION\" != \"0.14.0\" ]; then ./pip3 uninstall h11 -y; fi; ./pip3 install h11==0.14.0"
|
||
|
||
# 定义处理wsproto的命令变量
|
||
cmd_wsproto = "cd /www/server/panel/pyenv/bin && source activate && WSPROTO_VERSION=$(./pip3 show wsproto | grep -i Version | awk '{print $2}') && if [ \"$WSPROTO_VERSION\" != \"1.2.0\" ]; then ./pip3 uninstall wsproto -y; fi; ./pip3 install wsproto==1.2.0"
|
||
public.ExecShell(cmd_h11)
|
||
public.ExecShell(cmd_wsproto)
|
||
if os.path.exists(self.tip_file):
|
||
os.remove(self.tip_file)
|
||
else:
|
||
public.set_module_logs("nodes_installed_9", "set_used_status")
|
||
public.writeFile(self.tip_file, "True")
|
||
return public.return_message(0, 0, "Setup successful")
|
||
|
||
|
||
@staticmethod
|
||
def remove_ssh_conf(get):
|
||
node_id = get.get("node_id/d", 0)
|
||
ServerNodeDB().remove_node_ssh_conf(node_id)
|
||
return public.return_message(0, 0, "Deleted successfully")
|
||
|
||
@staticmethod
|
||
def set_ssh_conf(get):
|
||
"""设置ssh配置信息"""
|
||
host = get.get("host/s", "")
|
||
port = get.get("port/d", 22)
|
||
username = get.get("username/s", "root")
|
||
password = get.get("password/s", "")
|
||
pkey = get.get("pkey/s", "")
|
||
pkey_passwd = get.get("pkey_passwd/s", "")
|
||
node_id = get.get("node_id/d", 0)
|
||
test_case = get.get("test_case/d", 0)
|
||
|
||
if not node_id and not test_case:
|
||
return public.return_message(-1, 0, "Node does not exist")
|
||
|
||
if not host and node_id:
|
||
host = ServerNode.get_node_ip(node_id)
|
||
if not username:
|
||
username = "root"
|
||
if not host or not username or not port:
|
||
return public.return_message(-1, 0, "Host IP, host port, and user name cannot be empty")
|
||
if not password and not pkey:
|
||
return public.return_message(-1, 0, "Password or key cannot be empty")
|
||
|
||
res = test_ssh_config(host, port, username, password, pkey, pkey_passwd)
|
||
if res:
|
||
return public.return_message(-1, 0, res)
|
||
if test_case:
|
||
return public.return_message(0, 0, "Test successful")
|
||
ServerNodeDB().set_node_ssh_conf(node_id, {
|
||
"host": host,
|
||
"port": port,
|
||
"username": username,
|
||
"password": password,
|
||
"pkey": pkey,
|
||
"pkey_passwd": pkey_passwd
|
||
})
|
||
return public.return_message(0, 0, "Setup successful")
|
||
|
||
@staticmethod
|
||
def get_sshd_port(get):
|
||
node_id = get.get("node_id/d", 0)
|
||
srv = ServerNode.new_by_id(node_id)
|
||
if not srv:
|
||
return public.return_message(-1, 0, "Node does not exist")
|
||
|
||
port = srv.get_sshd_port()
|
||
if not port:
|
||
port = 22
|
||
return public.return_message(0, 0, {"port": port})
|
||
|
||
|
||
@staticmethod
|
||
def restart_bt_panel(get):
|
||
node_id = get.get("node_id/d", 0)
|
||
srv = ServerNode.new_by_id(node_id)
|
||
if not srv:
|
||
return public.return_message(-1, 0, "Node does not exist")
|
||
if srv.is_local:
|
||
return public.return_message(-1, 0, "The local node does not support this operation")
|
||
ret = srv.restart_bt_panel()
|
||
if ret.get("status"):
|
||
return public.return_message(0, 0, ret.get("msg"))
|
||
else:
|
||
return public.return_message(-1, 0, ret.get("msg"))
|
||
|
||
|
||
@staticmethod
|
||
def server_reboot(get):
|
||
node_id = get.get("node_id/d", 0)
|
||
srv = ServerNode.new_by_id(node_id)
|
||
if not srv:
|
||
return public.return_message(-1, 0, "Node does not exist")
|
||
if srv.is_local:
|
||
return public.return_message(-1, 0, "The local node does not support this operation")
|
||
repo = ServerMonitorRepo()
|
||
if repo.is_reboot_wait(srv.node_server_ip):
|
||
return public.return_message(-1, 0, "Node is restarting, please try again later")
|
||
ret = srv.server_reboot()
|
||
if ret.get("status"):
|
||
return public.return_message(0, 0, ret.get("msg"))
|
||
else:
|
||
return public.return_message(-1, 0, ret.get("msg")) |