Initial YakPanel commit

This commit is contained in:
Niranjan
2026-04-07 02:04:22 +05:30
commit 2826d3e7f3
5359 changed files with 1390724 additions and 0 deletions

View File

1169
mod/project/java/groupMod.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
import sys
if "/www/server/panel" not in sys.path:
sys.path.insert(0, "/www/server/panel")
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
from mod.project.java.groupMod import Group
def start_group(g_id: str):
g = Group(g_id)
g.real_run_start()
def stop_group(g_id: str):
g = Group(g_id)
g.real_run_stop()
if __name__ == '__main__':
if len(sys.argv) >= 3:
action = sys.argv[1]
group_id = sys.argv[2]
else:
print("Parameter error")
exit(1)
if action == "start":
start_group(group_id)
else:
stop_group(group_id)

View File

@@ -0,0 +1,575 @@
import os
import re
import shutil
import sys
from typing import List, Optional, Union, Tuple
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from mod.base.web_conf.util import listen_ipv6, get_log_path, GET_CLASS, service_reload
from mod.base.web_conf import NginxDomainTool, ApacheDomainTool
class JavaNginxTool:
def __init__(self):
self._panel_path = "/www/server/panel"
self._vhost_path = "{}/vhost".format(self._panel_path)
self._nginx_bak_path = "/var/tmp/springboot/nginx_conf_backup"
if not os.path.exists(self._nginx_bak_path):
os.makedirs(self._nginx_bak_path, 0o600)
def set_nginx_config(self, project_data: dict, domains: List[Tuple[str, Union[str, int]]],
use_ssl: bool = False, force_ssl=False):
if use_ssl:
use_http2_on = public.is_change_nginx_http2()
use_http3 = public.is_nginx_http3()
else:
use_http2_on = False
use_http3 = False
project_config = project_data["project_config"]
if project_config['java_type'] == "springboot":
project_path = project_data["project_config"]["jar_path"]
else:
project_path = project_data["path"]
if os.path.isfile(project_path):
project_path = os.path.dirname(project_path)
port_set = set()
domain_set = set()
use_ipv6 = listen_ipv6()
listen_ports_list = []
for d, p in domains:
if str(p) == "443": # 443 端口特殊处理
continue
if str(p) not in port_set:
listen_ports_list.append(" listen {};".format(str(p)))
if use_ipv6:
listen_ports_list.append(" listen [::]:{};".format(str(p)))
port_set.add(str(p))
domain_set.add(d)
if use_ssl:
if not use_http2_on:
http2 = " http2"
else:
http2 = ""
listen_ports_list.append(" http2 on;")
listen_ports_list.append(" listen 443 ssl{};".format(http2))
if use_ipv6:
listen_ports_list.append(" listen [::]:443 ssl{};".format(http2))
if use_http3:
listen_ports_list.append(" listen 443 quic;")
if use_ipv6:
listen_ports_list.append(" listen [::]:443 quic;")
listen_ports = "\n".join(listen_ports_list).strip()
static_conf = self._build_static_conf(project_config, project_path)
proxy_conf = self._build_proxy_conf(project_config)
ssl_conf = "#error_page 404/404.html;"
if use_ssl:
ssl_conf += "\n" + self._build_ssl_conf(project_config, use_http3=use_http3, force_ssl=force_ssl)
nginx_template_file = "{}/template/nginx/java_mod_http.conf".format(self._vhost_path)
nginx_conf_file = "{}/nginx/java_{}.conf".format(self._vhost_path, project_data["name"])
nginx_template = public.ReadFile(nginx_template_file)
if not isinstance(nginx_template, str):
return "读取模版文件失败"
nginx_conf = nginx_template.format(
listen_ports=listen_ports,
domains=" ".join(domain_set),
site_path=project_path,
site_name=project_data["name"],
panel_path=self._panel_path,
log_path=get_log_path(),
ssl_conf=ssl_conf,
static_conf=static_conf,
proxy_conf=proxy_conf,
)
rewrite_file = "{}/rewrite/java_{}.conf".format(self._vhost_path, project_data["name"])
if not os.path.exists(rewrite_file):
public.writeFile(rewrite_file, '# 请将伪静态规则或自定义NGINX配置填写到此处\n')
apply_check = "{}/nginx/well-known/{}.conf".format(self._vhost_path, project_data["name"])
if not os.path.exists(os.path.dirname(apply_check)):
os.makedirs(os.path.dirname(apply_check), 0o600)
if not os.path.exists(apply_check):
public.writeFile(apply_check, '')
public.writeFile(nginx_conf_file, nginx_conf)
return None
@staticmethod
def _build_proxy_conf(project_config: dict) -> str:
if "proxy_info" not in project_config:
return ""
proxy_info = project_config["proxy_info"]
proxy_conf_list = []
if not proxy_info:
return ""
ng_proxy = ''' #PROXY-START{proxy_dir}
location {proxy_dir} {{{rewrite}
proxy_pass {proxy_url};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;{add_headers}
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
proxy_set_header X-Host $host:$server_port;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 86400s;
proxy_send_timeout 30s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}}
#PROXY-END{proxy_dir}'''
for i in proxy_info:
if i.get("status", False):
continue
rewrite = ""
if "rewrite" in i and i["rewrite"].get("status", False):
rewrite = i["rewrite"]
src_path = i["src_path"]
if not src_path.endswith("/"):
src_path += "/"
target_path = rewrite["target_path"]
if target_path.endswith("/"):
target_path += target_path[:-1]
rewrite = "\n rewrite ^{}(.*)$ {}/$1 break;".format(src_path, target_path)
add_headers = ""
if "add_headers" in i:
header_tmp = " add_header {} {};"
add_headers_list = [header_tmp.format(h["k"], h["v"]) for h in i["add_headers"] if
"k" in h and "v" in h]
add_headers = "\n".join(add_headers_list)
if add_headers:
add_headers = "\n" + add_headers
proxy_conf_list.append(ng_proxy.format(
proxy_dir=i["proxy_dir"],
rewrite=rewrite,
add_headers=add_headers,
proxy_url="http://127.0.0.1:{}".format(i["proxy_port"]),
))
return ("\n".join(proxy_conf_list) + "\n").lstrip()
@staticmethod
def _build_static_conf(project_config: dict, default_path: str) -> str:
if project_config['java_type'] == "springboot" and "static_info" in project_config:
static_info = project_config["static_info"]
if not static_info.get("status", False):
return ""
index_str = "index.html"
index = static_info.get("index", "")
if index:
if isinstance(index, list):
index_str = " ".join(index)
elif isinstance(index, str):
index_str = " ".join([i.strip() for i in index.split(",") if i.strip()])
path = static_info.get("path")
if not path:
path = default_path
try_file = ''
if static_info.get("use_try_file", True):
try_file = " try_files $uri $uri/ /index.html;\n"
static_conf = (
"location / {\n"
" root %s;\n"
" index %s;\n%s"
" }"
) % (path, index_str, try_file)
return static_conf
return ""
def _build_ssl_conf(self, project_config: dict, use_http3=False, force_ssl=False) -> str:
force_ssl_str = ""
if force_ssl:
force_ssl_str = '''
#HTTP_TO_HTTPS_START
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
#HTTP_TO_HTTPS_END'''
http3_header = ""
if use_http3:
http3_header = '''\n add_header Alt-Svc 'quic=":443"; h3=":443"; h3-27=":443";h3-29=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';'''
return ''' ssl_certificate {vhost_path}/cert/{project_name}/fullchain.pem;
ssl_certificate_key {vhost_path}/cert/{project_name}/privkey.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";{http3_header}
error_page 497 https://$host$request_uri;{force_ssl}'''.format(
vhost_path=self._vhost_path,
project_name=project_config["project_name"],
http3_header=http3_header,
force_ssl=force_ssl_str,
)
def open_nginx_config_file(self, project_data: dict, domains: List[Tuple[str, str]], ) -> Optional[str]:
project_name = project_data["name"]
back_path = "{}/{}".format(self._nginx_bak_path, project_name)
target_file = "{}/nginx/java_{}.conf".format(self._vhost_path, project_name)
if os.path.isfile(target_file):
return
if os.path.isfile(back_path):
shutil.copyfile(back_path, target_file)
if os.path.isfile(target_file):
NginxDomainTool("java_").nginx_set_domain(project_name, *domains)
error_msg = public.checkWebConfig()
if not isinstance(error_msg, str): # 没有报错时直接退出
service_reload()
return
res = self.set_nginx_config(project_data, domains, use_ssl=False)
if not res:
service_reload()
return res
def close_nginx_config_file(self, project_data: dict) -> None:
project_name = project_data["name"]
back_path = "{}/{}".format(self._nginx_bak_path, project_name)
target_file = "{}/nginx/java_{}.conf".format(self._vhost_path, project_name)
if not os.path.isfile(target_file):
return
if os.path.isfile(back_path):
os.remove(back_path)
shutil.move(target_file, back_path)
service_reload()
def exists_nginx_ssl(self, project_name):
"""
判断项目是否配置Nginx SSL配置
"""
config_file = "{}/nginx/java_{}.conf".format(self._vhost_path, project_name)
if not os.path.exists(config_file):
return False, False
config_body = public.readFile(config_file)
if isinstance(config_body, str):
return False, False
is_ssl, is_force_ssl = False, False
if config_body.find('ssl_certificate') != -1:
is_ssl = True
if config_body.find('HTTP_TO_HTTPS_START') != -1:
is_force_ssl = True
return is_ssl, is_force_ssl
def set_static_path(self, project_data: dict) -> Optional[Union[bool, str]]:
project_path = project_data["project_config"]["jar_path"]
static_str = self._build_static_conf(project_data["project_config"], project_path)
ng_file = "{}/nginx/java_{}.conf".format(self._vhost_path, project_data["name"])
ng_conf = public.readFile(ng_file)
if not isinstance(ng_conf, str):
return "配置文件读取错误"
static_conf = "#STATIC-START 静态资源相关配置\n {}\n #STATIC-END".format(static_str)
rep_static = re.compile(r"#STATIC-START(.*\n){2,9}\s*#STATIC-END.*")
res = rep_static.search(ng_conf)
if res:
new_ng_conf = ng_conf.replace(res.group(), static_conf)
public.writeFile(ng_file, new_ng_conf)
error_msg = public.checkWebConfig()
if not isinstance(error_msg, str): # 没有报错时直接退出
service_reload()
return None
else:
public.writeFile(ng_file, ng_conf)
return 'WEB服务器配置配置文件错误ERROR:<br><font style="color:red;">' + \
error_msg.replace("\n", '<br>') + '</font>'
# 添加配置信息到配置文件中
rep_list = [
(re.compile(r"\s*#PROXY-LOCAl-START.*", re.M), True), # 添加到反向代理结尾的上面
(re.compile(r"\s*#REWRITE-END.*", re.M), False), # 添加到伪静态的下面
(re.compile(r"\s*#SSL-END.*", re.M), False), # 添加到SSL END的下面
]
# 使用正则匹配确定插入位置
def set_by_rep_idx(tmp_rep: re.Pattern, use_start: bool) -> bool:
tmp_res = tmp_rep.search(ng_conf)
if not tmp_res:
return False
if use_start:
new_conf = ng_conf[:tmp_res.start()] + static_conf + tmp_res.group() + ng_conf[tmp_res.end():]
else:
new_conf = ng_conf[:tmp_res.start()] + tmp_res.group() + static_conf + ng_conf[tmp_res.end():]
public.writeFile(ng_file, new_conf)
if public.get_webserver() == "nginx" and isinstance(public.checkWebConfig(), str):
public.writeFile(ng_file, ng_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
service_reload()
return None
else:
return False
class JavaApacheTool:
def __init__(self):
self._panel_path = "/www/server/panel"
self._vhost_path = "{}/vhost".format(self._panel_path)
self._apache_bak_path = "/var/tmp/springboot/httpd_conf_backup"
if not os.path.exists(self._apache_bak_path):
os.makedirs(self._apache_bak_path, 0o600)
def set_apache_config_for_ssl(self, project_data):
domains = public.M('domain').where('pid=?', (project_data["id"],)).select()
domain_list = [(i["name"], i["port"]) for i in domains]
return self.set_apache_config(project_data, domain_list, use_ssl=True)
def set_apache_config(self, project_data: dict, domains: List[Tuple[str, Union[str, int]]],
use_ssl: bool = False, force_ssl: bool = False):
name = project_data['name']
port_set = set()
domain_set = set()
for d, p in domains:
port_set.add(str(p))
domain_set.add(d)
domains_str = ' '.join(domain_set)
project_config = project_data["project_config"]
if project_config['java_type'] == "springboot":
project_path = project_data["project_config"]["jar_path"]
else:
project_path = project_data["path"]
if os.path.isfile(project_path):
project_path = os.path.dirname(project_path)
apache_template_file = "{}/template/apache/java_mod_http.conf".format(self._vhost_path)
apache_conf_file = "{}/apache/java_{}.conf".format(self._vhost_path, name)
apache_template = public.ReadFile(apache_template_file)
if not isinstance(apache_template, str):
return "读取模版文件失败"
apache_conf_list = []
proxy_conf = self._build_proxy_conf(project_config)
for p in port_set:
apache_conf_list.append(apache_template.format(
site_path=project_path,
server_name='{}.{}'.format(p, project_path),
domains=domains_str,
log_path=get_log_path(),
server_admin='admin@{}'.format(name),
port=p,
ssl_config='',
project_name=name,
proxy_conf=proxy_conf,
))
if use_ssl:
ssl_config = '''SSLEngine On
SSLCertificateFile {vhost_path}/cert/{project_name}/fullchain.pem
SSLCertificateKeyFile {vhost_path}/cert/{project_name}/privkey.pem
SSLCipherSuite EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5
SSLProtocol All -SSLv2 -SSLv3 -TLSv1
SSLHonorCipherOrder On'''.format(project_name=name, vhost_path=public.get_vhost_path())
if force_ssl:
ssl_config += '''
#HTTP_TO_HTTPS_START
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{SERVER_PORT} !^443$
RewriteRule (.*) https://%{SERVER_NAME}$1 [L,R=301]
</IfModule>
#HTTP_TO_HTTPS_END'''
apache_conf_list.append(apache_template.format(
site_path=project_path,
server_name='{}.{}'.format("443", project_path),
domains=domains_str,
log_path=get_log_path(),
server_admin='admin@{}'.format(name),
port="443",
ssl_config=ssl_config,
project_name=name,
proxy_conf=proxy_conf,
))
apache_conf = '\n'.join(apache_conf_list)
public.writeFile(apache_conf_file, apache_conf)
ApacheDomainTool.apache_add_ports(*port_set)
return None
@staticmethod
def _build_proxy_conf(project_config: dict) -> str:
if "proxy_info" not in project_config:
return ""
proxy_info = project_config["proxy_info"]
proxy_conf_list = []
if not proxy_info:
return ""
ap_proxy = ''' #PROXY-START{proxy_dir}
<IfModule mod_proxy.c>
ProxyRequests Off
SSLProxyEngine on
ProxyPass {proxy_dir} {proxy_url}/
ProxyPassReverse {proxy_dir} {proxy_url}/
RequestHeader set Host "%{Host}e"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}e"
RequestHeader set X-Forwarded-For "%{X-Forwarded-For}e"
RequestHeader setifempty X-Forwarded-For "%{REMOTE_ADDR}e"
</IfModule>
#PROXY-END{proxy_dir}'''
for i in proxy_info:
if i.get("status", False):
continue
proxy_conf_list.append(ap_proxy.format(
proxy_dir=i["proxy_dir"],
proxy_url="http://127.0.0.1:{}".format(i["proxy_port"]),
))
return ("\n".join(proxy_conf_list) + "\n").lstrip()
def open_apache_config_file(self, project_data: dict, domains: List[Tuple[str, str]]) -> Optional[str]:
project_name = project_data["name"]
back_path = "{}/{}".format(self._apache_bak_path, project_name)
target_file = "{}/apache/java_{}.conf".format(self._vhost_path, project_name)
if os.path.isfile(target_file):
return
if os.path.isfile(back_path):
shutil.copyfile(back_path, target_file)
if os.path.isfile(target_file):
ApacheDomainTool("java_").apache_set_domain(project_name, *domains)
error_msg = public.checkWebConfig()
if not isinstance(error_msg, str): # 没有报错时直接退出
service_reload()
return
res = self.set_apache_config(
project_data,
domains=domains,
use_ssl=False,
)
if not res:
service_reload()
return res
def close_apache_config_file(self, project_data: dict) -> None:
project_name = project_data["name"]
back_path = "{}/{}".format(self._apache_bak_path, project_name)
target_file = "{}/apache/java_{}.conf".format(self._vhost_path, project_name)
if not os.path.isfile(target_file):
return
if os.path.isfile(back_path):
os.remove(back_path)
shutil.move(target_file, back_path)
service_reload()
def exists_apache_ssl(self, project_name) -> Tuple[bool, bool]:
"""
判断项目是否配置Apache SSL配置
"""
config_file = "{}/apache/java_{}.conf".format(self._vhost_path, project_name)
if not os.path.exists(config_file):
return False, False
config_body = public.readFile(config_file)
if not isinstance(config_body, str):
return False, False
is_ssl, is_force_ssl = False, False
if config_body.find('SSLCertificateFile') != -1:
is_ssl = True
if config_body.find('HTTP_TO_HTTPS_START') != -1:
is_force_ssl = True
return is_ssl, is_force_ssl
class JvavWebConfig:
def __init__(self):
self._ng_conf_onj = JavaNginxTool()
self._ap_conf_onj = JavaApacheTool()
self.ws_type = public.get_webserver()
def create_config(self, project_data: dict, domains: List[Tuple[str, Union[str, int]]],
use_ssl: bool = False, force_ssl=False):
ng_res = self._ng_conf_onj.set_nginx_config(project_data, domains, use_ssl, force_ssl=force_ssl)
ap_res = self._ap_conf_onj.set_apache_config(project_data, domains, use_ssl, force_ssl=force_ssl)
if self.ws_type == "nginx" and ng_res:
return ng_res
elif self.ws_type == "apache" and ap_res:
return ap_res
service_reload()
def _open_config_file(self, project_data: dict):
domain_list = public.M('domain').where('pid=?', (project_data["id"],)).field("name,port").select()
domains = [(i["name"], str(i["port"])) for i in domain_list]
if not domains:
return "域名不能为空"
ng_res = self._ng_conf_onj.open_nginx_config_file(project_data, domains)
ap_res = self._ap_conf_onj.open_apache_config_file(project_data, domains)
if self.ws_type == "nginx" and ng_res:
return ng_res
elif self.ws_type == "apache" and ap_res:
return ap_res
def _close_apache_config_file(self, project_data: dict) -> None:
self._ap_conf_onj.close_apache_config_file(project_data)
self._ng_conf_onj.close_nginx_config_file(project_data)
def _set_domain(self, project_data: dict, domains: List[Tuple[str, str]]) -> Optional[str]:
ng_res = NginxDomainTool("java_").nginx_set_domain(project_data["name"], *domains)
ap_res = ApacheDomainTool("java_").apache_set_domain(project_data["name"], *domains)
if self.ws_type == "nginx" and ng_res:
return ng_res
elif self.ws_type == "apache" and ap_res:
return ap_res
def _get_ssl_status(self, project_name) -> Tuple[bool, bool]:
if self.ws_type == "nginx":
return self._ng_conf_onj.exists_nginx_ssl(project_name)
elif self.ws_type == "apache":
return self._ap_conf_onj.exists_apache_ssl(project_name)
return False, False
def _set_static_path(self, project_data: dict):
if self.ws_type == "nginx":
res = self._ng_conf_onj.set_static_path(project_data)
if res is None:
return None
elif res is False:
err_msg = public.checkWebConfig()
if isinstance(err_msg, str):
return 'WEB服务器配置配置文件错误ERROR:<br><font style="color:red;">' + \
err_msg.replace("\n", '<br>') + '</font>'
return self._open_config_file(project_data)
else:
return res
return "只支持nginx设置静态路由"

Binary file not shown.

View File

@@ -0,0 +1,215 @@
#!/usr/bin/env python3
"""
Python interface to JMX. Uses local jar to pass commands to JMX and read JSON
results returned.
"""
import subprocess
import os
import json
from typing import List
from enum import Enum
import logging
# Full Path to Jar
JAR_PATH = os.path.dirname(os.path.realpath(__file__)) + '/JMXQuery-0.1.8.jar'
# Default Java path
DEFAULT_JAVA_PATH = 'java'
# Default timeout for running jar in seconds
DEFAULT_JAR_TIMEOUT = 10
logger = logging.getLogger(__name__)
class MetricType(Enum):
COUNTER = 'counter'
GAUGE = 'gauge'
class JMXQuery:
"""
A JMX Query which is used to fetch specific MBean attributes/values from the JVM. The object_name can support wildcards
to pull multiple metrics at once, for example '*:*' will bring back all MBeans and attributes in the JVM with their values.
You can set a metric name if you want to override the generated metric name created from the MBean path
"""
def __init__(self,
mBeanName: str,
attribute: str = None,
attributeKey: str = None,
value: object = None,
value_type: str = None,
metric_name: str = None,
metric_labels: dict = None):
self.mBeanName = mBeanName
self.attribute = attribute
self.attributeKey = attributeKey
self.value = value
self.value_type = value_type
self.metric_name = metric_name
self.metric_labels = metric_labels
def to_query_string(self) -> str:
"""
Build a query string to pass via command line to JMXQuery Jar
:return: The query string to find the MBean in format:
{mBeanName}/{attribute}/{attributeKey}
Example: java.lang:type=Memory/HeapMemoryUsage/init
"""
query = ""
if self.metric_name:
query += self.metric_name
if ((self.metric_labels != None) and (len(self.metric_labels) > 0)):
query += "<"
keyCount = 0
for key, value in self.metric_labels.items():
query += key + "=" + value
keyCount += 1
if keyCount < len(self.metric_labels):
query += ","
query += ">"
query += "=="
query += self.mBeanName
if self.attribute:
query += "/" + self.attribute
if self.attributeKey:
query += "/" + self.attributeKey
return query
def to_string(self):
string = ""
if self.metric_name:
string += self.metric_name
if ((self.metric_labels != None) and (len(self.metric_labels) > 0)):
string += " {"
keyCount = 0
for key, value in self.metric_labels.items():
string += key + "=" + value
keyCount += 1
if keyCount < len(self.metric_labels):
string += ","
string += "}"
else:
string += self.mBeanName
if self.attribute:
string += "/" + self.attribute
if self.attributeKey:
string += "/" + self.attributeKey
string += " = "
string += str(self.value) + " (" + self.value_type + ")"
return string
class JMXConnection(object):
"""
The main class that connects to the JMX endpoint via a local JAR to run queries
"""
def __init__(self, connection_uri: str, jmx_username: str = None, jmx_password: str = None, java_path: str = DEFAULT_JAVA_PATH):
"""
Creates instance of JMXQuery set to a specific connection uri for the JMX endpoint
:param connection_uri: The JMX connection URL. E.g. service:jmx:rmi:///jndi/rmi://localhost:7199/jmxrmi
:param jmx_username: (Optional) Username if JMX endpoint is secured
:param jmx_password: (Optional) Password if JMX endpoint is secured
:param java_path: (Optional) Provide an alternative Java path on the machine to run the JAR.
Default is 'java' which will use the machines default JVM
"""
self.connection_uri = connection_uri
self.jmx_username = jmx_username
self.jmx_password = jmx_password
self.java_path = java_path
def __run_jar(self, queries: List[JMXQuery], timeout) -> List[JMXQuery]:
"""
Run the JAR and return the results
:param query: The query
:return: The full command array to run via subprocess
"""
command = [self.java_path, '-jar', JAR_PATH, '-url', self.connection_uri, "-json"]
if (self.jmx_username):
command.extend(["-u", self.jmx_username, "-p", self.jmx_password])
queryString = ""
for query in queries:
queryString += query.to_query_string() + ";"
command.extend(["-q", queryString])
logger.debug("Running command: " + str(command))
jsonOutput = "[]"
try:
output = subprocess.run(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=timeout,
check=True)
jsonOutput = output.stdout.decode('utf-8')
except subprocess.TimeoutExpired as err:
logger.error("Error calling JMX, Timeout of " + str(err.timeout) + " Expired: " + err.output.decode('utf-8'))
except subprocess.CalledProcessError as err:
logger.error("Error calling JMX: " + err.output.decode('utf-8'))
raise err
logger.debug("JSON Output Received: " + jsonOutput)
metrics = self.__load_from_json(jsonOutput)
return metrics
def __load_from_json(self, jsonOutput: str) -> List[JMXQuery]:
"""
Loads the list of returned metrics from JSON response
:param jsonOutput: The JSON Array returned from the command line
:return: An array of JMXQuerys
"""
if "\n" in jsonOutput:
jsonOutput = jsonOutput.replace("\n", "")
if "\t" in jsonOutput:
jsonOutput = jsonOutput.replace("\t", "")
jsonMetrics = json.loads(jsonOutput)
metrics = []
for jsonMetric in jsonMetrics:
mBeanName = jsonMetric['mBeanName']
attribute = jsonMetric['attribute']
attributeType = jsonMetric['attributeType']
metric_name = None
if 'metricName' in jsonMetric:
metric_name = jsonMetric['metricName']
metric_labels = None
if 'metricLabels' in jsonMetric:
metric_labels = jsonMetric['metricLabels']
attributeKey = None
if 'attributeKey' in jsonMetric:
attributeKey = jsonMetric['attributeKey']
value = None
if 'value' in jsonMetric:
value = jsonMetric['value']
metrics.append(
JMXQuery(mBeanName, attribute, attributeKey, value, attributeType, metric_name, metric_labels))
return metrics
def query(self, queries: List[JMXQuery], timeout=DEFAULT_JAR_TIMEOUT) -> List[JMXQuery]:
"""
Run a list of JMX Queries against the JVM and get the results
:param queries: A list of JMXQuerys to query the JVM for
:return: A list of JMXQuerys found in the JVM with their current values
"""
return self.__run_jar(queries, timeout)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,600 @@
import copy
import os
import re
import sys
import json
import socket
import time
import traceback
import psutil
import errno
from typing import Optional, List
from threading import Thread
from urllib3.util import parse_url, Url
if "/www/server/panel" not in sys.path:
sys.path.insert(0, "/www/server/panel")
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
from mod.base import RealServer
from mod.base import json_response
from mod.project.java.projectMod import main as java_mod
from mod.project.java import utils
class ProjectUpdate:
def __init__(self, project_name: str, new_jar: str, new_port: int = None, run_time: int = None):
self.project_name = project_name
self.new_jar = new_jar
self.j_project = java_mod()
self.keep_path = self.j_project._java_project_path + "/keep"
if not os.path.exists(self.keep_path):
os.makedirs(self.keep_path, 0o755)
self.keep_log = "{}/{}.log".format(self.keep_path, self.project_name)
# 不停机更新时使用
self.new_port = new_port
self.run_time = run_time
self.keep_status = []
self.new_project_config = None
self.old_project_config = None
self.old_pro: Optional[psutil.Process] = None
self.new_pro: Optional[psutil.Process] = None
self.end = False
self.old_project_data = None
self.proxy_data = {
"scheme": "http"
}
@staticmethod
def new_suffix() -> str:
import uuid
return "_" + uuid.uuid4().hex[::4]
def start_spring_project(self, project_data: dict, write_systemd_file=True, need_wait=True, ) -> dict:
return self.j_project._start_spring_boot_project(project_data, write_systemd_file, need_wait)
def restart_update(self) -> dict:
project_data = self.j_project.get_project_find(self.project_name)
if not project_data:
return json_response(False, msg="The project does not exist")
project_config = project_data['project_config']
old_jar = project_config['project_jar']
if self.new_jar != old_jar:
if not os.path.isfile(self.new_jar):
return json_response(False, msg="项目jar包不存在")
project_config['jar_path'] = os.path.dirname(self.new_jar)
project_config['project_jar'] = self.new_jar
old_jar_name = os.path.basename(old_jar)
project_cmd_list = project_config['project_cmd'].split(" ")
for i in range(len(project_cmd_list)):
if old_jar_name in project_cmd_list[i]:
project_cmd_list[i] = self.new_jar
break
new_project_cmd = " ".join(project_cmd_list)
project_config['project_cmd'] = new_project_cmd
project_config["change_flag"] = True
s_admin = RealServer()
server_name = "spring_" + project_config["project_name"] + project_config.get("server_name_suffix", "")
if s_admin.daemon_status(server_name)["msg"] == "服务不存在!":
self.j_project.stop_by_kill_pid(project_data)
if os.path.isfile(project_config["pids"]):
os.remove(project_config["pids"])
return self.start_spring_project(project_data, write_systemd_file=True, need_wait=False)
if "change_flag" in project_config and project_config.get("change_flag", False):
del project_config["change_flag"]
s_admin.daemon_admin(server_name, "stop")
s_admin.del_daemon(server_name)
self.j_project.stop_by_kill_pid(project_data)
if os.path.isfile(project_config["pids"]):
os.remove(project_config["pids"])
public.M("sites").where("id=?", (project_data["id"],)).update(
{"project_config": json.dumps(project_config)}
)
return self.start_spring_project(project_data, write_systemd_file=True)
else:
return self.start_spring_project(project_data, write_systemd_file=False)
# 实际执行启动的线程
def run_task(self):
print("___________开始________________")
try:
res = self.start_new()
self.keep_status[0]["status"] = 1
if res:
self.keep_status[0]["msg"] = res
return
else:
self.keep_status[0]["msg"] = "新实例已启动新实例pid:{}".format(self.new_pro.pid)
res = self.set_nginx_upstream()
self.keep_status[1]["status"] = 1
if res:
self.stop_new()
self.keep_status[1]["msg"] = res
return
else:
self.keep_status[1]["msg"] = "Nginx已配置完成轮询设置您可以访问新实例了"
res = self.wait_time()
self.keep_status[2]["status"] = 1
if res:
self.keep_status[2]["msg"] = res
return
else:
self.keep_status[2]["msg"] = "等待时间结束,新实例已启动成功"
res = self.stop_old()
self.keep_status[3]["status"] = 1
self.keep_status[3]["msg"] = res if res else "停止旧实例成功,项目更新已结束"
public.M("sites").where("id=?", (self.old_project_data["id"],)).update(
{"project_config": json.dumps(self.new_project_config)}
)
except:
print(traceback.format_exc())
pass
def stop_new(self):
new_server_name = "spring_" + self.project_name + self.new_project_config.get("server_name_suffix", "")
RealServer().server_admin(new_server_name, "stop")
RealServer().del_daemon(new_server_name)
if self.new_pro and self.new_pro.is_running():
self.new_pro.kill()
def start_new(self) -> Optional[str]:
self.keep_status[0]["status"] = -1
self.new_project_config['server_name_suffix'] = self.new_suffix()
self.new_project_config['pids'] = "{}/pids/{}.pid".format(
self.j_project._java_project_vhost, self.project_name + self.new_project_config['server_name_suffix']
)
if not self.new_port or self.new_port in self.old_listen_port() or \
utils.check_port_with_net_connections(self.new_port):
self.new_port = utils.create_a_not_used_port()
old_jar = self.old_project_config['project_jar']
if self.new_jar != old_jar:
if not os.path.isfile(self.new_jar):
return "项目jar包不存在"
self.new_project_config['jar_path'] = os.path.dirname(self.new_jar)
self.new_project_config['project_jar'] = self.new_jar
old_jar_name = os.path.basename(old_jar)
project_cmd_list = self.new_project_config['project_cmd'].split(" ")
for i in range(len(project_cmd_list)):
if old_jar_name in project_cmd_list[i]:
project_cmd_list[i] = self.new_jar
break
new_project_cmd = " ".join(project_cmd_list)
self.new_project_config['project_cmd'] = new_project_cmd
if "--server.port=" in self.new_project_config['project_cmd']:
self.new_project_config['project_cmd'] = re.sub(
r"--server\.port=\d+",
"--server.port={}".format(self.new_port),
self.new_project_config['project_cmd']
)
else:
self.new_project_config['project_cmd'] += " --server.port={}".format(self.new_port)
self.old_project_data["project_config"] = self.new_project_config
self.start_spring_project(self.old_project_data, write_systemd_file=True)
time.sleep(1)
new_pid = self.j_project.get_project_pid(self.old_project_data)
if not new_pid:
return "项目启动失败"
self.new_pro = psutil.Process(new_pid)
self.keep_status[0]["msg"] = "新实例pid为:{}".format(new_pid)
# 开始等待进程启动
server_name = "spring_" + self.project_name + self.new_project_config.get("server_name_suffix", "")
wait_num = 1
for i in range(5 * 60 * 2 - 2):
if self.end:
RealServer().server_admin(server_name, "stop")
RealServer().del_daemon(server_name)
return "退出操作"
if not self.new_pro.is_running():
RealServer().del_daemon(server_name)
return "项目启动失败"
conns = self.new_pro.connections()
for c in conns:
if c.status == "LISTEN" and c.laddr.port == self.new_port:
return
self.keep_status[0]["msg"] = "新实例pid为:{}, 正在等待该进程监听端口:{}, 已等待{}s".format(new_pid, self.new_port, wait_num)
wait_num += 0.5
time.sleep(0.5)
RealServer().server_admin(server_name, "stop")
RealServer().del_daemon(server_name)
return "启动超时"
def old_listen_port(self) -> List[int]:
connects = self.old_pro.connections()
res = []
for i in connects:
if i.status == "LISTEN":
res.append(i.laddr.port)
return res
def set_nginx_upstream(self) -> Optional[str]:
self.keep_status[1]["status"] = -1
ng_file = "/www/server/panel/vhost/nginx/java_{}.conf".format(self.project_name)
res = public.checkWebConfig()
if res is not True:
return "Nginx配置文件错误无法开始轮询配置"
ng_data = public.readFile(ng_file)
if not isinstance(ng_data, str):
return "Nginx配置文件读取错误无法开始轮询配置"
old_proxy_res = None
for tmp_res in re.finditer(r"\s*proxy_pass\s+(?P<url>\S+)\s*;", ng_data, re.M):
url: Url = parse_url(tmp_res.group("url"))
if url.hostname in ("127.0.0.1", "localhost", "0.0.0.0") and url.port in self.old_listen_port():
old_proxy_res = tmp_res
self.proxy_data["scheme"] = url.scheme
self.proxy_data["old_port"] = url.port
if not old_proxy_res:
return "未找到原实例的代理配置"
upstream_file = "/www/server/panel/vhost/nginx/java_{}_upstream.conf".format(self.project_name)
public.writeFile(upstream_file, """
upstream {}_backend {{
server 127.0.0.1:{};
server 127.0.0.1:{};
}}
""".format(self.project_name, self.proxy_data["old_port"], self.new_port))
new_config = ng_data.replace(old_proxy_res.group(), "\n proxy_pass {}://{}_backend;".format(
self.proxy_data["scheme"], self.project_name))
public.writeFile(ng_file, new_config)
res = public.checkWebConfig()
if res is not True:
public.writeFile(ng_file, ng_data)
return "Nginx配置文件错误无法开始轮询配置"
else:
public.serviceReload()
def wait_time(self):
self.keep_status[2]["status"] = -1
if not self.run_time:
self.run_time = 10 * 60
for i in range(self.run_time):
if self.end:
return "退出操作"
self.keep_status[2]["msg"] = "已进入轮询测试等待"
if i > 0:
self.keep_status[2]["msg"] = "已进入轮询测试等待,已等待{}s, 共需等待{}s".format(i, self.run_time)
time.sleep(1)
if not self.new_pro.is_running():
return "新示例已退出,无法继续执行操作"
return None
def select_new_or_old(self, option: str):
if option == "use_new":
self.keep_status[2]["status"] = 1
self.keep_status[2]["msg"] = "已跳过等待时间,使用新实例运行"
res = self.stop_old()
public.M("sites").where("id=?", (self.old_project_data["id"],)).update(
{"project_config": json.dumps(self.new_project_config)}
)
self.keep_status[3]["status"] = 1
self.keep_status[3]["msg"] = res if res else "停止旧实例成功,项目更新已结束"
return {"status": False if res else True, "msg": res if res else "停止旧实例成功,项目更新已结束"}
self.keep_status[2]["status"] = 1
self.keep_status[2]["msg"] = "已跳过等待时间,使用原实例运行"
self.keep_status[3]["name"] = "停止新实例"
self.keep_status[3]["status"] = 1
ng_file = "/www/server/panel/vhost/nginx/java_{}.conf".format(self.project_name)
ng_data = public.readFile(ng_file)
if not isinstance(ng_data, str):
return {"status": False, "msg": "Nginx配置文件读取错误无法取消轮询并使用原实例"}
res = public.checkWebConfig()
if res is not True:
return {"status": False, "msg": "Nginx配置文件错误无法取消轮询并使用原实例"}
upstream_file = "/www/server/panel/vhost/nginx/java_{}_upstream.conf".format(self.project_name)
new_config = ng_data.replace(
"{}_backend".format(self.project_name),
"127.0.0.1:{}".format(self.proxy_data["old_port"])
)
public.writeFile(ng_file, new_config)
res = public.checkWebConfig()
if res is not True:
public.writeFile(ng_file, ng_data)
return {"status": False, "msg": "Nginx配置文件设置错误无法取消轮询并使用原实例"}
else:
os.remove(upstream_file)
public.serviceReload()
self.stop_new()
return {"status": True, "msg": "停止新实例成功,项目更新已结束"}
def stop_old(self):
self.keep_status[3]["status"] = -1
ng_file = "/www/server/panel/vhost/nginx/java_{}.conf".format(self.project_name)
ng_data = public.readFile(ng_file)
if not isinstance(ng_data, str):
return "Nginx配置文件读取错误无法取消轮询使用新实例"
res = public.checkWebConfig()
if res is not True:
return "Nginx配置文件错误无法取消轮询使用新实例"
old_proxy_res = None
for tmp_res in re.finditer(r"\s*proxy_pass\s+(?P<url>\S+)\s*;", ng_data, re.M):
if tmp_res.group("url").find("{}_backend".format(self.project_name)):
old_proxy_res = tmp_res
if not old_proxy_res:
return "未找到轮询的代理配置"
upstream_file = "/www/server/panel/vhost/nginx/java_{}_upstream.conf".format(self.project_name)
if os.path.isfile(upstream_file):
os.remove(upstream_file)
new_config = ng_data.replace(old_proxy_res.group(), "\n proxy_pass {}://127.0.0.1:{};".format(
self.proxy_data["scheme"], self.new_port))
public.writeFile(ng_file, new_config)
res = public.checkWebConfig()
if res is not True:
public.writeFile(ng_file, ng_data)
return "Nginx配置文件错误无法结束轮询配置"
else:
public.serviceReload()
old_server_name = "spring_" + self.project_name + self.old_project_config.get("server_name_suffix", "")
RealServer().server_admin(old_server_name, "stop")
RealServer().del_daemon(old_server_name)
if self.old_pro and self.old_pro.is_running():
self.old_pro.kill()
return None
def keep_update(self):
pid_file = "{}/{}.pid".format(self.keep_path, self.project_name)
log_file = "{}/{}.log".format(self.keep_path, self.project_name)
pid = os.getpid()
public.writeFile(pid_file, str(pid))
if os.path.exists(log_file):
os.remove(log_file)
project_data = self.j_project.get_project_find(self.project_name)
if not project_data:
return json_response(False, msg="The project does not exist")
project_config = project_data['project_config']
self.old_project_data = project_data
self.old_project_config = project_config
self.new_project_config = copy.deepcopy(project_config)
try:
self.old_pro = psutil.Process(self.j_project.get_project_pid(project_data))
except:
pass
if not self.old_pro:
return json_response(False, msg="项目未启动")
self.end = False
self.keep_status = [
{
"name": "启动新实例",
"status": 0,
"msg": "",
},
{
"name": "设置Nginx轮询",
"status": 0,
"msg": "",
},
{
"name": "等待并检查新实例",
"status": 0,
"msg": "",
},
{
"name": "停止旧实例",
"status": 0,
"msg": "",
}
]
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socket_file = "{}/{}.socket".format(self.keep_path, self.project_name)
# 清理旧的socket文件如果存在
if os.path.exists(socket_file):
os.remove(socket_file)
# 设置为非阻塞
sock.bind(socket_file)
sock.setblocking(False) # 0表示非阻塞1表示阻塞
sock.listen(2)
update_run_task = Thread(target=self.run_task)
update_run_task.start()
while True:
if not update_run_task.is_alive():
public.writeFile(log_file, json.dumps(self.keep_status))
break
try:
# 读取客户端发送的数据
conn, _ = sock.accept()
data = conn.recv(1024)
except socket.error as e:
if e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK):
raise e
time.sleep(0.1)
continue
if not data:
time.sleep(0.1)
continue
# 打印接收到的数据
print("Received:", data.decode())
data_str = data.decode()
if data_str == "stop_new":
if self.keep_status[0]["status"] == -1:
self.end = True
update_run_task.join()
public.writeFile(log_file, json.dumps(self.keep_status))
conn.sendall(json.dumps({
"status": True,
"msg": "已关闭更新任务,并停止新实例"
}).encode())
break
else:
conn.sendall(json.dumps({
"status": False,
"msg": "新实例启动完成,已加入轮询,无法继续执行该操作"
}).encode())
elif data_str == "status":
conn.sendall(json.dumps(self.keep_status).encode())
elif data_str in ("use_new", "use_old"):
if self.keep_status[2]["status"] != -1:
conn.sendall(json.dumps({
"status": False,
"msg": "已超过轮询等待时间,无法执行该操作"
}).encode())
else:
self.end = True
update_run_task.join()
public.writeFile(log_file, json.dumps(self.keep_status))
res = self.select_new_or_old(data_str)
conn.sendall(json.dumps(res).encode())
time.sleep(0.1)
# 关闭服务器端socket
sock.close()
# 清理旧的socket文件如果存在
if os.path.exists(socket_file):
os.remove(socket_file)
def get_keep_status(self):
try:
log_file = "{}/{}.log".format(self.keep_path, self.project_name)
pid_file = "{}/{}.pid".format(self.keep_path, self.project_name)
log_data = public.readFile(log_file)
data = None
if isinstance(log_data, str):
try:
data = json.loads(log_data)
except:
pass
if data:
return json_response(True, data={
"running": False,
"keep_msg": data
})
pid_data = public.readFile(pid_file)
er_msg = "没有正在进行的更新任务"
if not isinstance(pid_data, str):
return json_response(False, msg=er_msg)
try:
pid = int(pid_data)
if not psutil.pid_exists(pid):
return json_response(False, msg=er_msg)
except:
return json_response(False, msg=er_msg)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect("{}/{}.socket".format(self.keep_path, self.project_name))
except Exception:
public.print_log(public.get_error_info())
return json_response(False, msg="链接错误请尝试强制停止更新")
data = b"status"
sock.sendall(data)
# 接收响应
sock.settimeout(1)
response = sock.recv(1024 * 2)
sock.close()
try:
data = json.loads(response.decode())
except:
public.print_log(public.get_error_info())
return json_response(False, msg="链接错误请尝试强制停止更新")
return json_response(True, data={
"running": True,
"keep_msg": data
})
except:
public.print_log(public.get_error_info())
return json_response(False, msg="链接错误请尝试强制停止更新")
def keep_option(self, option: str) -> dict:
try:
pid_file = "{}/{}.pid".format(self.keep_path, self.project_name)
pid_data = public.readFile(pid_file)
er_msg = "没有正在进行的更新任务, 无法执行操作"
if not isinstance(pid_data, str) or pid_data == "0":
return json_response(False, msg=er_msg)
try:
pid = int(pid_data)
if not psutil.pid_exists(pid):
return json_response(False, msg=er_msg)
except:
return json_response(False, msg=er_msg)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect("{}/{}.socket".format(self.keep_path, self.project_name))
except Exception:
return json_response(False, msg="链接错误,无法执行操作,请尝试强制停止更新")
sock.sendall(option.encode())
# 接收响应
sock.settimeout(10)
response = sock.recv(1024)
sock.close()
try:
data = json.loads(response.decode())
except:
public.print_log(public.get_error_info())
return json_response(False, msg="链接错误请尝试强制停止更新")
if isinstance(data, dict):
return json_response(data['status'], msg=data['msg'])
else:
return json_response(False, msg="链接错误请尝试强制停止更新")
except:
public.print_log(public.get_error_info())
return json_response(False, msg="链接错误请尝试强制停止更新")
if __name__ == '__main__':
def run_main(project_name: str, new_jar: str, new_port: int, run_time: int,):
pu = ProjectUpdate(project_name, new_jar=new_jar, run_time=run_time, new_port=new_port)
pu.keep_update()
if len(sys.argv) == 5:
run_main(sys.argv[1], sys.argv[2], int(sys.argv[3]), int(sys.argv[4]))

View File

@@ -0,0 +1,644 @@
import os
import re
import json
from typing import Optional, Union, List, Dict, Any
from mod.base.web_conf.util import check_server_config, write_file, read_file, service_reload
from mod.base import json_response
from urllib3.util import Url, parse_url
import public
class RealServerProxy:
panel_path = "/www/server/panel"
default_headers = (
"Host", "X-Real-IP", "X-Forwarded-For", "REMOTE-HOST", "X-Host", "X-Scheme", "Upgrade", "Connection"
)
def __init__(self, project_data: dict):
self.config_prefix: str = "java_"
site_name = project_data["name"]
self.project_id = project_data["id"]
self.project_config = project_data["project_config"]
self._config: Optional[List[dict]] = None
self._ng_file: str = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
self._ap_file: str = "{}/vhost/apache/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
self.site_name = site_name
self.ws_type = public.get_webserver()
@staticmethod
def new_id() -> str:
from uuid import uuid4
return uuid4().hex[::3]
@property
def config(self) -> List[dict]:
if self._config is None:
if "proxy_info" in self.project_config:
self._config = self.project_config["proxy_info"]
else:
self._config = []
return self._config
def save_config(self):
if self._config is not None:
self.project_config["proxy_info"] = self._config
public.M("sites").where("id=?", (self.project_id,)).update(
{"project_config": json.dumps(self.project_config)}
)
# 检查代理是否存在
def _check_even(self, proxy_conf: dict, is_modify) -> Optional[str]:
if is_modify is True:
for i in self.config:
if i["proxy_dir"] == proxy_conf["proxy_dir"] and i["proxy_id"] != proxy_conf["proxy_id"]:
return '指定反向代理名称或代理文件夹已存在'
if i["proxy_port"] == proxy_conf["proxy_port"] and i["proxy_id"] != proxy_conf["proxy_id"]:
return '指定反向代理端口已存在对应的代理'
else:
for i in self.config:
if i["proxy_port"] == proxy_conf["proxy_port"]:
return '指定反向代理端口已存在对应的代理'
def check_args(self, get, is_modify=False) -> Union[str, dict]:
err_msg = check_server_config()
if isinstance(err_msg, str):
return 'WEB服务器配置配置文件错误ERROR:<br><font style="color:red;">' + \
err_msg.replace("\n", '<br>') + '</font>'
data = {
"proxy_dir": "/",
"status": 1,
"proxy_id": self.new_id(),
"rewrite": {
"status": False,
"src_path": "",
"target_path": "",
},
"add_headers": [],
}
try:
data["site_name"] = get.site_name.strip()
if "proxy_dir" in get:
data["proxy_dir"] = get.proxy_dir.strip()
if "proxy_id" in get:
data["proxy_id"] = get.proxy_id.strip()
data["proxy_port"] = int(get.proxy_port)
data["status"] = int(get.status.strip())
if hasattr(get, "rewrite"):
data["rewrite"] = get.rewrite
if isinstance(get.rewrite, str):
data["rewrite"] = json.loads(get.rewrite)
if hasattr(get, "add_headers"):
data["add_headers"] = get.add_headers
if isinstance(get.add_headers, str):
data["add_headers"] = json.loads(get.add_headers)
except:
public.print_log(public.get_error_info())
return "Parameter error"
if not 1 < data["proxy_port"] < 65536:
return '代理端口范围错误'
if not data["proxy_dir"].endswith("/"):
data["proxy_dir"] += "/"
evn_msg = self._check_even(data, is_modify)
if isinstance(evn_msg, str):
return evn_msg
rep_re_key = re.compile(r'''[?=\[\])(*&^%$#@!~`{}><,'"\\]+''')
special = r'''?=[])(*&^%$#@!~`{}><\'"'''
# 检测代理目录格式
if rep_re_key.search(data["proxy_dir"]):
return "代理路由不能有以下特殊符号" + special
if not isinstance(data["rewrite"], dict):
return "路由重写配置错误"
if "status" not in data["rewrite"] or not data["rewrite"]["status"]:
data["rewrite"] = {
"status": False,
"src_path": "",
"target_path": "",
}
else:
if not ("src_path" in data["rewrite"] and "target_path" in data["rewrite"]):
return "路由重写参数配置错误"
if not isinstance(data["rewrite"]["src_path"], str) or not isinstance(data["rewrite"]["target_path"], str):
return "路由重写参数配置错误"
if rep_re_key.search(data["rewrite"]["src_path"]):
return "路由重写匹配路由不能有以下特殊符号" + special
if rep_re_key.search(data["rewrite"]["target_path"]):
return "路由重写目标路由不能有以下特殊符号" + special
if not isinstance(data["add_headers"], list):
return "自定义代理头配置错误"
else:
rep_blank_space = re.compile(r"\s+")
for h in data["add_headers"]:
if "k" not in h or "v" not in h:
return "自定义代理头配置错误"
if not isinstance(h["k"], str) or not isinstance(h["v"], str):
return "自定义代理头配置错误"
if rep_blank_space.search(h["k"]) or rep_blank_space.search(h["v"]):
return "代理头配置中不能包含有空格"
if h["k"] in self.default_headers:
return '代理头配置中不能包含有默认头【{}'.format(h["k"])
return data
def check_location(self, proxy_dir: str) -> Optional[str]:
# 伪静态文件路径
rewrite_conf_path = "%s/vhost/rewrite/%s%s.conf" % (self.panel_path, self.config_prefix, self.site_name)
rep_location = re.compile(r"s*location\s+(\^~\s*)?%s\s*{" % proxy_dir)
for i in [rewrite_conf_path, self._ng_file]:
conf = read_file(i)
if isinstance(conf, str) and rep_location.search(conf):
return '伪静态/站点主配置文件已经存路径【{}】的配置'.format(proxy_dir)
@staticmethod
def _set_nginx_proxy_base():
file = "/www/server/nginx/conf/proxy.conf"
setup_path = "/www/server"
if not os.path.exists(file):
conf = '''proxy_temp_path %s/nginx/proxy_temp_dir;
proxy_cache_path %s/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:10m inactive=1d max_size=5g;
client_body_buffer_size 512k;
proxy_connect_timeout 60;
proxy_read_timeout 60;
proxy_send_timeout 60;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
proxy_cache cache_one;''' % (setup_path, setup_path)
write_file(file, conf)
conf = read_file(file)
if conf and conf.find('include proxy.conf;') == -1:
conf = re.sub(r"include\s+mime\.types;", "include mime.types;\n\tinclude proxy.conf;", conf)
write_file(file, conf)
# websocket前置map
map_file = "/www/server/panel/vhost/nginx/0.websocket.conf"
if not os.path.exists(map_file):
write_file(map_file, '''
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}''')
@staticmethod
def build_proxy_conf(proxy_data: dict) -> str:
ng_proxy = '''
#PROXY-START{proxy_dir}
location {proxy_dir} {{{rewrite}
proxy_pass {proxy_url};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;{add_headers}
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
proxy_set_header X-Host $host:$server_port;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 86400s;
proxy_send_timeout 30s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}}
#PROXY-END{proxy_dir}
'''
rewrite = ""
if "rewrite" in proxy_data and proxy_data["rewrite"].get("status", False):
rewrite = proxy_data["rewrite"]
src_path = rewrite["src_path"]
if not src_path.endswith("/"):
src_path += "/"
target_path = rewrite["target_path"]
if target_path.endswith("/"):
target_path = target_path[:-1]
rewrite = "\n rewrite ^{}(.*)$ {}/$1 break;".format(src_path, target_path)
add_headers = ""
if "add_headers" in proxy_data:
header_tmp = " proxy_set_header {} {};"
add_headers_list = [header_tmp.format(h["k"], h["v"]) for h in proxy_data["add_headers"] if
"k" in h and "v" in h]
add_headers = "\n".join(add_headers_list)
if add_headers:
add_headers = "\n" + add_headers
# 构造替换字符串
proxy_dir = proxy_data["proxy_dir"]
proxy_site = "http://127.0.0.1:{}".format(proxy_data["proxy_port"])
proxy = ng_proxy.format(
proxy_dir=proxy_dir,
proxy_url=proxy_site,
rewrite=rewrite,
add_headers=add_headers,
)
return proxy
def add_nginx_proxy(self, proxy_data: dict) -> Optional[str]:
ng_conf = read_file(self._ng_file)
if not ng_conf:
return "Nginx配置文件不存在"
proxy_str = self.build_proxy_conf(proxy_data)
# 添加配置信息到配置文件中
rep_list = [
(re.compile(r"\s*#PROXY-LOCAl-END.*", re.M), True), # 添加到反向代理结尾的上面
(re.compile(r"\s*#ROXY-END.*", re.M), False), # 添加到其他的反向代理的下面
(re.compile(r"\s*#\s*HTTP反向代理相关配置结束\s*<<<.*", re.M), False), # 添加到其他的反向代理的下面
(re.compile(r"\s*include\s*/www/server/panel/vhost/rewrite/.*(\s*#.*)?"), False),
# 添加到伪静态的下面
# (re.compile(r"(#.*)?\s*location\s+/\.well-known/\s*{"), True), # 添加到location /.well-known/上面
]
# 使用正则匹配确定插入位置
def set_by_rep_idx(tmp_rep: re.Pattern, use_start: bool) -> bool:
tmp_res = tmp_rep.search(ng_conf)
if not tmp_res:
return False
if use_start:
new_conf = ng_conf[:tmp_res.start()] + proxy_str + tmp_res.group() + ng_conf[tmp_res.end():]
else:
new_conf = ng_conf[:tmp_res.start()] + tmp_res.group() + proxy_str + ng_conf[tmp_res.end():]
write_file(self._ng_file, new_conf)
if self.ws_type == "nginx" and check_server_config() is not None:
write_file(self._ng_file, ng_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
break
else:
return "无法在配置文件中定位到需要添加的项目"
def _unset_nginx_proxy(self, proxy_data) -> Optional[str]:
ng_conf = read_file(self._ng_file)
if not isinstance(ng_conf, str):
return "配置文件不存在"
proxy_dir = proxy_data["proxy_dir"]
rep_start_end = re.compile(r"\s*#PROXY-START%s(.|\n)*?#PROXY-END%s[^\n]*" % (proxy_dir, proxy_dir))
if rep_start_end.search(ng_conf):
new_ng_conf = rep_start_end.sub("", ng_conf)
write_file(self._ng_file, new_ng_conf)
if self.ws_type == "nginx":
err_msg = check_server_config()
if isinstance(err_msg, str):
write_file(self._ng_file, ng_conf)
return err_msg
else:
return
rep_location = re.compile(r"(\s*#.*?)\s*location\s+(\^~\s*)?%s\s*{" % proxy_dir)
res = rep_location.search(ng_conf)
if res:
end_idx = self.find_nginx_block_end(ng_conf, res.end() + 1)
if not end_idx:
return
block = ng_conf[res.start(): end_idx]
if block.find("proxy_pass") == -1: # 如果这块内不包含proxy_pass 则跳过
return
# 异除下一个注释行
res_end = re.search(r"\s*#PROXY-END.*", ng_conf[end_idx + 1:])
if res_end:
end_idx += res_end.end()
new_ng_conf = ng_conf[:res.start()] + ng_conf[end_idx + 1:]
write_file(self._ng_file, new_ng_conf)
if self.ws_type == "nginx":
err_msg = check_server_config()
if isinstance(err_msg, str):
write_file(self._ng_file, ng_conf)
return err_msg
@staticmethod
def find_nginx_block_end(data: str, start_idx: int) -> Optional[int]:
if len(data) < start_idx + 1:
return None
level = 1
line_start = 0
for i in range(start_idx + 1, len(data)):
if data[i] == '\n':
line_start = i + 1
if data[i] == '{' and line_start and data[line_start: i].find("#") == -1: # 没有注释的下一个{
level += 1
elif data[i] == '}' and line_start and data[line_start: i].find("#") == -1: # 没有注释的下一个}
level -= 1
if level == 0:
return i
return None
@staticmethod
def build_apache_conf(proxy_data: dict) -> str:
return '''
#PROXY-START{proxy_dir}
<IfModule mod_proxy.c>
ProxyRequests Off
SSLProxyEngine on
ProxyPass {proxy_dir} {url}/
ProxyPassReverse {proxy_dir} {url}/
RequestHeader set Host "%{{Host}}e"
RequestHeader set X-Real-IP "%{{REMOTE_ADDR}}e"
RequestHeader set X-Forwarded-For "%{{X-Forwarded-For}}e"
RequestHeader setifempty X-Forwarded-For "%{{REMOTE_ADDR}}e"
</IfModule>
#PROXY-END{proxy_dir}
'''.format(proxy_dir=proxy_data["proxy_dir"], url="http://127.0.0.1:{}".format(proxy_data["proxy_port"]))
def add_apache_proxy(self, proxy_data: dict) -> Optional[str]:
ap_conf = read_file(self._ap_file)
if not ap_conf:
return "Apache配置文件不存在"
proxy_str = self.build_apache_conf(proxy_data)
# 添加配置信息到配置文件中
rep_list = [
(re.compile(r"#ROXY-END[^\n]*\n"), False), # 添加到其他的反向代理的下面
(re.compile(r"#\s*HTTP反向代理相关配置结束\s*<<<[^\n]*\n"), False), # 添加到其他的反向代理的下面
(
re.compile(r"\s*(#SSL[^\n]*)?\s*<IfModule\s*alias_module>[^\n]*\s*.*/.well-known/[^\n]*\s*</IfModule>"),
True # 添加到location /.well-known/上面
),
(re.compile(r"\s*</VirtualHost>[^\n]*\n?"), True),
]
# 使用正则匹配确定插入位置
def set_by_rep_idx(tmp_rep: re.Pattern, use_start: bool) -> bool:
new_conf_list = []
change_flag = False
start_idx = 0
for tmp in tmp_rep.finditer(ap_conf):
change_flag = True
new_conf_list.append(ap_conf[start_idx:tmp.start()])
start_idx = tmp.end()
if use_start:
new_conf_list.append(proxy_str)
new_conf_list.append(tmp.group())
else:
new_conf_list.append(tmp.group())
new_conf_list.append(proxy_str)
if not change_flag:
return False
new_conf_list.append(ap_conf[start_idx:])
write_file(self._ap_file, "".join(new_conf_list))
if self.ws_type == "apache" and check_server_config() is not None:
write_file(self._ap_file, ap_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
break
else:
return "无法在配置文件中定位到需要添加的项目"
def remove_apache_proxy(self, proxy_data) -> Optional[str]:
ap_conf = read_file(self._ap_file)
if not isinstance(ap_conf, str):
return "配置文件不存在"
proxy_dir = proxy_data["proxy_dir"]
rep_start_end = re.compile(r"\s*#PROXY-START%s(.|\n)*?#PROXY-END%s[^\n]*" % (proxy_dir, proxy_dir))
if rep_start_end.search(ap_conf):
new_ap_conf = rep_start_end.sub("", ap_conf)
write_file(self._ap_file, new_ap_conf)
if self.ws_type == "apache":
err_msg = check_server_config()
if isinstance(err_msg, str):
write_file(self._ap_file, ap_conf)
return err_msg
else:
return
rep_if_mod = re.compile(
r"(\s*#.*)?\s*<IfModule mod_proxy\.c>\s*(.*\n){3,5}\s*"
r"ProxyPass\s+%s\s+\S+/\s*(.*\n){1,2}\s*</IfModule>(\s*#.*)?" % proxy_dir)
res = rep_if_mod.search(ap_conf)
if res:
new_ap_conf = rep_if_mod.sub("", ap_conf)
write_file(self._ap_file, new_ap_conf)
if self.ws_type == "apache":
err_msg = check_server_config()
if isinstance(err_msg, str):
write_file(self._ap_file, ap_conf)
return err_msg
def create_proxy(self, proxy_data: dict) -> Optional[str]:
for i in self.config:
if i["proxy_dir"] == proxy_data["proxy_dir"]:
proxy_data["proxy_id"] = i["proxy_id"]
return self.modify_proxy(proxy_data)
if self.ws_type == "nginx":
err_msg = self.check_location(proxy_data["proxy_dir"])
if err_msg:
return json_response(False, err_msg)
self._set_nginx_proxy_base()
error_msg = self.add_nginx_proxy(proxy_data)
if self.ws_type == "nginx" and error_msg:
return error_msg
error_msg = self.add_apache_proxy(proxy_data)
if self.ws_type == "apache" and error_msg:
return error_msg
self.config.append(proxy_data)
self.save_config()
service_reload()
def modify_proxy(self, proxy_data: dict) -> Optional[str]:
idx = None
for index, i in enumerate(self.config):
if i["proxy_id"] == proxy_data["proxy_id"] and i["site_name"] == proxy_data["site_name"]:
idx = index
break
if idx is None:
return "未找到该id的反向代理配置"
if proxy_data["proxy_dir"] != self.config[idx]["proxy_dir"] and self.ws_type == "nginx":
err_msg = self.check_location(proxy_data["proxy_dir"])
if err_msg:
return json_response(False, err_msg)
self._set_nginx_proxy_base()
error_msg = self._unset_nginx_proxy(self.config[idx])
if self.ws_type == "nginx" and error_msg:
return error_msg
error_msg = self.remove_apache_proxy(self.config[idx])
if self.ws_type == "apache" and error_msg:
return error_msg
error_msg = self.add_nginx_proxy(proxy_data)
if self.ws_type == "nginx" and error_msg:
return error_msg
error_msg = self.add_apache_proxy(proxy_data)
if self.ws_type == "apache" and error_msg:
return error_msg
self.config[idx] = proxy_data
self.save_config()
service_reload()
def remove_proxy(self, site_name, proxy_id, multiple=False) -> Optional[str]:
idx = None
for index, i in enumerate(self.config):
if i["proxy_id"] == proxy_id and i["site_name"] == site_name:
idx = index
if idx is None:
return "未找到该名称的反向代理配置"
err_msg = self._unset_nginx_proxy(self.config[idx])
if err_msg and self.ws_type == "nginx":
return err_msg
error_msg = self.remove_apache_proxy(self.config[idx])
if self.ws_type == "apache" and error_msg:
return error_msg
del self.config[idx]
self.save_config()
if not multiple:
service_reload()
def get_proxy_list_by_nginx(self) -> List[Dict[str, Any]]:
ng_conf = read_file(self._ng_file)
if not isinstance(ng_conf, str):
return []
rep_location = re.compile(r"\s*location\s+([=*~^]*\s+)?(?P<path>\S+)\s*{")
proxy_location_path_info = {}
for tmp in rep_location.finditer(ng_conf):
end_idx = self.find_nginx_block_end(ng_conf, tmp.end() + 1)
if end_idx and ng_conf[tmp.start(): end_idx].find("proxy_pass") != -1:
p = tmp.group("path")
if not p.endswith("/"):
p += "/"
proxy_location_path_info[p] = (tmp.start(), end_idx)
res_pass = re.compile(r"proxy_pass\s+(?P<pass>\S+)\s*;", re.M)
remove_list = []
local_host = ("127.0.0.1", "localhost", "0.0.0.0")
for i in self.config:
if i["proxy_dir"] in proxy_location_path_info:
start_idx, end_idx = proxy_location_path_info[i["proxy_dir"]]
block = ng_conf[start_idx: end_idx]
res_pass_res = res_pass.search(block)
if res_pass_res:
url = parse_url(res_pass_res.group("pass"))
if isinstance(url, Url) and url.hostname in local_host and url.port == i["proxy_port"]:
i["status"] = True
proxy_location_path_info.pop(i["proxy_dir"])
continue
remove_list.append(i)
need_save = False
for i in remove_list:
self.config.remove(i)
need_save = True
for path, (start_idx, end_idx) in proxy_location_path_info.items():
block = ng_conf[start_idx: end_idx]
res_pass_res = res_pass.search(block)
if res_pass_res:
url = parse_url(res_pass_res.group("pass"))
if isinstance(url, Url) and url.hostname in ("127.0.0.1", "localhost", "0.0.0.0"):
self.config.insert(0, {
"proxy_id": self.new_id(),
"site_name": self.site_name,
"proxy_dir": "/",
"proxy_port": url.port,
"status": 1,
"rewrite": {
"status": False,
"src_path": "",
"target_path": "",
},
"add_headers": [],
})
need_save = True
if need_save:
self.save_config()
return self.config
def get_proxy_list_by_apache(self) -> List[Dict[str, Any]]:
ap_conf = read_file(self._ap_file)
if not isinstance(ap_conf, str):
return []
rep_proxy_pass = r"ProxyPass\s+%s\s+\S+/"
mian_location_use = False
for i in self.config:
if i["proxy_dir"] == "/":
mian_location_use = True
rep_l = re.search(rep_proxy_pass % i["proxy_dir"], ap_conf, re.M)
if rep_l:
i["status"] = 1
else:
i["status"] = 0
if not mian_location_use:
res_l = re.search(
r"\s*<IfModule mod_proxy\.c>\s*(.*\n){3,5}\s*"
r"ProxyPass\s+/\s+(?P<pass>\S+)/\s*(.*\n){1,2}\s*</IfModule>", ap_conf)
if not res_l:
return self.config
url = parse_url(res_l.group("pass"))
if isinstance(url, Url) and url.hostname in ("127.0.0.1", "localhost", "0.0.0.0"):
self.config.insert(0, {
"proxy_id": self.new_id(),
"site_name": self.site_name,
"proxy_dir": "/",
"proxy_port": url.port,
"status": 1,
"rewrite": {
"status": False,
"src_path": "",
"target_path": "",
},
"add_headers": [],
})
self.save_config()
return self.config
def get_proxy_list(self) -> List[Dict[str, Any]]:
if self.ws_type == "nginx":
return self.get_proxy_list_by_nginx()
else:
return self.get_proxy_list_by_apache()

File diff suppressed because it is too large Load Diff

946
mod/project/java/utils.py Normal file
View File

@@ -0,0 +1,946 @@
import json
import re
import sys
import time
import zipfile
import os
import yaml
import psutil
import platform
from xml.etree.ElementTree import Element, ElementTree, parse, XMLParser
from typing import Optional, Dict, Tuple, AnyStr, List, Any
import threading
import itertools
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
if "/www/server/panel" not in sys.path:
sys.path.insert(0, "/www/server/panel")
import public
def get_jar_war_config(jar_war_file: str) -> Optional[List[Tuple[str, AnyStr]]]:
"""获取jar文件中的配置文件"""
if not os.path.exists(jar_war_file):
return None
if not zipfile.is_zipfile(jar_war_file): # 判断是否为zip文件
return None
# 打开jar文件
res_list = []
with zipfile.ZipFile(jar_war_file, 'r') as jar:
for i in jar.namelist():
# 查询所有文件中可能是配置文件的项目
if i.endswith("application.yaml") or i.endswith("application.yml"):
with jar.open(i) as f:
res_list.append((i, f.read()))
if not res_list:
return None
return res_list
def to_utf8(file_data_list: List[Tuple[str, AnyStr]]) -> List[Tuple[str, str]]:
res_list = []
for i, data in file_data_list:
if isinstance(data, bytes):
try:
new_data = data.decode("utf-8")
except:
continue
else:
res_list.append((i, new_data))
return res_list
def parse_application_yaml(conf_data_list: List[Tuple[str, AnyStr]]) -> List[Tuple[str, Dict]]:
res_list = []
for i, data in conf_data_list:
d = yaml.safe_load(data)
if isinstance(d, dict):
res_list.append((i, d))
return res_list
# 接收一个jdk路径并将其规范化
def normalize_jdk_path(jdk_path: str) -> Optional[str]:
if jdk_path.endswith("/java"):
jdk_path = os.path.dirname(jdk_path)
if jdk_path.endswith("/bin"):
jdk_path = os.path.dirname(jdk_path)
if jdk_path.endswith("/jre"):
jdk_path = os.path.dirname(jdk_path)
if not os.path.isdir(jdk_path):
return None
if not os.path.exists(os.path.join(jdk_path, "bin/java")):
return None
return jdk_path
def test_jdk(jdk_path: str) -> bool:
java_bin = os.path.join(jdk_path, "bin/java")
if os.path.exists(java_bin):
out, err = public.ExecShell("{} -version 2>&1".format(java_bin)) # type: str, str
if out.lower().find("version") != -1:
return True
return False
class TomCat:
def __init__(self, tomcat_path: str):
self.path = tomcat_path.rstrip("/") # 移除多余的右"/" 统一管理
self._jdk_path: Optional[str] = None
self._config_xml: Optional[ElementTree] = None
self._bt_tomcat_conf: Optional[dict] = None
self._log_file = None
self._version = None
@property
def jdk_path(self) -> Optional[str]:
p = os.path.join(self.path, "bin/daemon.sh")
if not os.path.exists(p):
return None
tmp_data = public.readFile(p)
if isinstance(tmp_data, str):
rep_deemon_sh = re.compile(r"^JAVA_HOME=(?P<path>.*)\n", re.M)
re_res_jdk_path = rep_deemon_sh.search(tmp_data)
if re_res_jdk_path:
self._jdk_path = re_res_jdk_path.group("path").strip()
self._jdk_path = normalize_jdk_path(self._jdk_path)
return self._jdk_path
return None
def version(self) -> Optional[int]:
if isinstance(self._version, int):
return self._version
v_file = os.path.join(self.path, "version.pl")
if os.path.isfile(v_file):
ver = public.readFile(v_file)
if isinstance(ver, str):
try:
ver_int = int(ver.split(".")[0])
self._version = ver_int
return self._version
except:
pass
return None
@property
def log_file(self) -> str:
if self._log_file is not None:
return self._log_file
default_file = os.path.join(self.path, "logs/catalina-daemon.out")
target_sh = os.path.join(self.path, "bin/daemon.sh")
file_data = public.readFile(target_sh)
conf_path = os.path.join(self.path, "conf/logpath.conf")
if not isinstance(file_data, str):
return default_file
rep = re.compile(r'''\n\s?test ?"\.\$CATALINA_OUT" ?= ?\. +&& +CATALINA_OUT=['"](?P<path>\S+)['"]''')
if rep.search(file_data):
self._log_file = rep.search(file_data).group("path")
public.writeFile(conf_path, os.path.dirname(self._log_file))
return self._log_file
if os.path.isfile(conf_path):
path = public.readFile(conf_path)
else:
return default_file
log_file = os.path.join(path, "catalina-daemon.out")
if os.path.exists(log_file):
self._log_file = log_file
return self._log_file
ver = self.version()
if ver:
log_file = os.path.join(path, "catalina-daemon-{}.out".format(ver))
return log_file
else:
return os.path.join(path, "catalina-daemon.out")
@property
def bt_tomcat_conf(self) -> Optional[dict]:
if self._bt_tomcat_conf is None:
p = os.path.join(self.path, "bt_tomcat.json")
if not os.path.exists(p):
self._bt_tomcat_conf = {}
return self._bt_tomcat_conf
try:
self._bt_tomcat_conf = json.loads(public.readFile(p))
except:
self._bt_tomcat_conf = {}
return self._bt_tomcat_conf
def save_bt_tomcat_conf(self):
if self._bt_tomcat_conf is not None:
p = os.path.join(self.path, "bt_tomcat.json")
public.writeFile(p, json.dumps(self._bt_tomcat_conf))
def change_log_path(self, log_path: str, prefix: str = "") -> bool:
log_path = log_path.rstrip("/")
target_sh = os.path.join(self.path, "bin/daemon.sh")
if not os.path.exists(target_sh):
return False
file_data = public.readFile(target_sh)
if not isinstance(file_data, str):
return False
rep = re.compile(r'''\n ?test ?"\.\$CATALINA_OUT" ?= ?\. && {0,3}CATALINA_OUT="[^\n]*"[^\n]*\n''')
if prefix and not prefix.startswith("-"):
prefix = "-{}".format(prefix)
repl = '\ntest ".$CATALINA_OUT" = . && CATALINA_OUT="{}/catalina-daemon{}.out"\n'.format(log_path, prefix)
file_data = rep.sub(repl, file_data)
public.writeFile(target_sh, file_data)
conf_path = os.path.join(self.path, "conf/logpath.conf")
public.WriteFile(conf_path, log_path)
return True
@property
def config_xml(self) -> Optional[ElementTree]:
if self._config_xml is None:
p = os.path.join(self.path, "conf/server.xml")
if not os.path.exists(p):
return None
self._config_xml = parse(p, parser=XMLParser(encoding="utf-8"))
return self._config_xml
def set_port(self, port: int) -> bool:
if self.config_xml is None:
return False
conf_elem = self.config_xml.findall("Service/Connector")
if conf_elem is None:
return False
for i in conf_elem:
if 'protocol' in i.attrib and 'port' in i.attrib:
if i.attrib['protocol'] == 'HTTP/1.1':
i.attrib['port'] = str(port)
return True
return False
def pid(self) -> Optional[int]:
pid_file = os.path.join(self.path, 'logs/catalina-daemon.pid')
if os.path.exists(pid_file):
# 使用psutil判断进程是否在运行
try:
pid = public.readFile(pid_file)
return int(pid)
except:
return None
return None
def port(self) -> int:
if self.config_xml is None:
return 0
for i in self.config_xml.findall("Service/Connector"):
if i.attrib.get("protocol") == "HTTP/1.1" and 'port' in i.attrib:
return int(i.attrib.get("port"))
return 8080
@property
def installed(self) -> bool:
start_path = os.path.join(self.path, 'bin/daemon.sh')
conf_path = os.path.join(self.path, 'conf/server.xml')
if not os.path.exists(self.path):
return False
if not os.path.isfile(start_path):
return False
if not os.path.isfile(conf_path):
return False
return True
def running(self) -> bool:
pid = self.pid()
if pid:
try:
p = psutil.Process(pid)
return p.is_running()
except:
return False
return False
def status(self) -> dict:
return {
"status": os.path.exists(self.path) and os.path.exists(os.path.join(self.path, "bin/daemon.sh")),
"jdk_path": self.jdk_path,
"path": self.path,
"running": self.running(),
"port": self.port(),
"stype": "built" if os.path.exists(os.path.join(self.path, "conf/server.xml")) else "uninstall"
}
def save_config_xml(self) -> bool:
if self.config_xml is None:
return False
p = os.path.join(self.path, "conf/server.xml")
def _indent(elem: Element, level=0):
i = "\n" + level * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
_indent(elem, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
_indent(self.config_xml.getroot())
self.config_xml.write(p, encoding="utf-8", xml_declaration=True)
return True
def host_by_name(self, name: str) -> Optional[Element]:
if self.config_xml is None:
return None
engines = self.config_xml.findall("Service/Engine")
if not engines:
return None
engine = engines[0]
for h in engine:
if h.tag == "Host" and h.attrib.get("name", None) == name:
return h
return None
def add_host(self, name: str, path: str) -> bool:
if self.config_xml is None:
return False
if not os.path.exists(path):
os.makedirs(path)
if self.host_by_name(name):
return False
engine = self.config_xml.findall("Service/Engine")
if not engine:
return False
path_name = ""
if os.path.isfile(path):
app_base = os.path.dirname(path)
if path.endswith(".war"):
path_name = os.path.basename(path).rsplit(".", 1)[0]
else:
app_base = path
host = Element("Host", attrib={
"appBase": app_base,
"autoDeploy": "true",
"name": name,
"unpackWARs": "true",
"xmlNamespaceAware": "false",
"xmlValidation": "false",
})
context = Element("Context", attrib={
"docBase": path,
"path": path_name,
"reloadable": "true",
"crossContext": "true",
})
host.append(context)
engine[0].append(host)
return True
def set_host_path_by_name(self, name: str, path: str) -> bool:
if self.config_xml is None:
return False
for i in self.config_xml.findall("Service/Engine/Host"):
if i.attrib.get("name", None) != name:
continue
for j in i:
if j.tag == "Context":
j.attrib["docBase"] = path
return True
return False
def remove_host(self, name: str) -> bool:
if self.config_xml is None:
return False
target_host = self.host_by_name(name)
if not target_host:
return False
engine = self.config_xml.findall("Service/Engine")
if not engine:
return False
engine[0].remove(target_host)
return True
def mutil_remove_host(self, name_list: List[str]) -> bool:
if self.config_xml is None:
return False
for name in name_list:
self.remove_host(name)
return False
def start(self, by_user: str = "root") -> bool:
if not self.running():
daemon_file = os.path.join(self.path, "bin/daemon.sh")
if not os.path.isfile(self.log_file):
public.ExecShell("touch {}".format(self.log_file))
public.ExecShell("chown {}:{} {}".format(by_user, by_user, self.log_file))
public.ExecShell("bash {} start".format(daemon_file), user=by_user)
return self.running()
def stop(self) -> bool:
if self.running():
daemon_file = os.path.join(self.path, "bin/daemon.sh")
public.ExecShell("bash {} stop".format(daemon_file))
return not self.running()
def restart(self, by_user: str = "root") -> bool:
daemon_file = os.path.join(self.path, "bin/daemon.sh")
if self.running():
public.ExecShell("bash {} stop".format(daemon_file))
if not os.path.isfile(self.log_file):
public.ExecShell("touch {}".format(self.log_file))
public.ExecShell("chown {}:{} {}".format(by_user, by_user, self.log_file))
public.ExecShell("bash {} start".format(daemon_file), user=by_user)
return self.running()
def replace_jdk(self, jdk_path: str) -> Optional[str]:
jdk_path = normalize_jdk_path(jdk_path)
if not jdk_path:
return "jdk路径错误或无法识别"
deemon_sh_path = "{}/bin/daemon.sh".format(self.path)
if not os.path.isfile(deemon_sh_path):
return 'Tomcat启动文件丢失!'
deemon_sh_data = public.readFile(deemon_sh_path)
if not isinstance(deemon_sh_data, str):
return 'Tomcat启动文件读取失败!'
# deemon_sh
rep_deemon_sh = re.compile(r"^JAVA_HOME=(?P<path>.*)\n", re.M)
re_res_deemon_sh = rep_deemon_sh.search(deemon_sh_data)
if not re_res_deemon_sh:
return 'Tomcat启动文件解析失败!'
jsvc_make_path = None
for i in os.listdir(self.path + "/bin"):
tmp_dir = "{}/bin/{}".format(self.path, i)
if i.startswith("commons-daemon") and os.path.isdir(tmp_dir):
make_path = tmp_dir + "/unix"
if os.path.isdir(make_path):
jsvc_make_path = make_path
break
if jsvc_make_path is None:
return 'Jsvc文件丢失!'
# 重装jsvc
if os.path.isfile(self.path + "/bin/jsvc"):
os.rename(self.path + "/bin/jsvc", self.path + "/bin/jsvc_back")
if os.path.isfile(jsvc_make_path + "/jsvc"):
os.remove(jsvc_make_path + "/jsvc")
shell_str = r'''
cd {}
make clean
./configure --with-java={}
make
'''.format(jsvc_make_path, jdk_path)
public.ExecShell(shell_str)
if os.path.isfile(jsvc_make_path + "/jsvc"):
os.rename(jsvc_make_path + "/jsvc", self.path + "/bin/jsvc")
public.ExecShell("chmod +x {}/bin/jsvc".format(self.path))
os.remove(self.path + "/bin/jsvc_back")
else:
os.rename(self.path + "/bin/jsvc_back", self.path + "/bin/jsvc")
return 'Jsvc编译失败!'
new_deemon_sh_data = deemon_sh_data[:re_res_deemon_sh.start()] + (
'JAVA_HOME={}\n'.format(jdk_path)
) + deemon_sh_data[re_res_deemon_sh.end():]
public.writeFile(deemon_sh_path, new_deemon_sh_data)
return None
def reset_tomcat_server_config(self, port: int):
ret = '''<Server port="{}" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector connectionTimeout="20000" port="{}" protocol="HTTP/1.1" redirectPort="8490" />
<Engine defaultHost="localhost" name="Catalina">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />
</Realm>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt" />
</Host>
</Engine>
</Service>
</Server>'''.format(create_a_not_used_port(), port)
public.WriteFile(self.path + '/conf/server.xml', ret)
@staticmethod
def _get_os_version() -> str:
# 获取Centos
if os.path.exists('/usr/bin/yum') and os.path.exists('/etc/yum.conf'):
return 'Centos'
# 获取Ubuntu
if os.path.exists('/usr/bin/apt-get') and os.path.exists('/usr/bin/dpkg'):
return 'Ubuntu'
return 'Unknown'
@classmethod
def async_install_tomcat_new(cls, version: str, jdk_path: Optional[str]) -> Optional[str]:
os_ver = cls._get_os_version()
if version == "7" and os_ver == 'Ubuntu':
return '操作系统不支持!'
if jdk_path:
jdk_path = normalize_jdk_path(jdk_path)
if not jdk_path:
return 'jdk路径错误或无法识别'
if not test_jdk(jdk_path):
return '指定的jdk不可用'
if not jdk_path:
jdk_path = ''
shell_str = (
'rm -rf /tmp/1.sh && '
'/usr/local/curl/bin/curl -o /tmp/1.sh %s/install/src/webserver/shell/new_jdk.sh && '
'bash /tmp/1.sh install %s %s'
) % (public.get_url(), version, jdk_path)
if not os.path.exists("/tmp/panelTask.pl"): # 如果当前任务队列并未执行,就把日志清空
public.writeFile('/tmp/panelExec.log', '')
soft_name = "Java项目Tomcat-" + version
task_id = public.M('tasks').add(
'id,name,type,status,addtime,execstr',
(None, '安装[{}]'.format(soft_name), 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), shell_str))
cls._create_install_wait_msg(task_id, version)
@staticmethod
def _create_install_wait_msg(task_id: int, version: str):
from panel_msg.msg_file import message_mgr
file_path = "/tmp/panelExec.log"
if not os.path.exists(file_path):
public.writeFile(file_path, "")
soft_name = "Java项目Tomcat-" + version
data = {
"soft_name": soft_name,
"install_status": "等待安装" + soft_name,
"file_name": file_path,
"self_type": "soft_install",
"status": 0,
"task_id": task_id
}
title = "等待安装" + soft_name
res = message_mgr.collect_message(title, ["Java环境管理", soft_name], data)
if isinstance(res, str):
public.WriteLog("消息盒子", "安装信息收集失败")
return None
return res
def bt_tomcat(ver: int) -> Optional[TomCat]:
if ver not in (7, 8, 9, 10) and ver not in ("7", "8", "9", "10"):
return None
return TomCat(tomcat_path="/usr/local/bttomcat/tomcat%d" % int(ver))
def site_tomcat(site_name: str) -> Optional[TomCat]:
tomcat_path = os.path.join("/www/server/bt_tomcat_web", site_name)
if not os.path.exists(tomcat_path):
return None
return TomCat(tomcat_path=tomcat_path)
class JDKManager:
def __init__(self):
self._versions_list: Optional[List[str]] = None
self._custom_jdk_list: Optional[List[str]] = None
self._jdk_path = "/www/server/java"
self._custom_file = "/www/server/panel/data/get_local_jdk.json"
if not os.path.exists(self._jdk_path):
os.makedirs(self._jdk_path, 0o755)
@property
def versions_list(self) -> List[str]:
if self._versions_list:
return self._versions_list
jdk_json_file = '/www/server/panel/data/jdk.json'
tip_file = '/www/server/panel/data/jdk.json.pl'
try:
last_refresh = int(public.readFile(tip_file))
except ValueError:
last_refresh = 0
versions_data = public.readFile(jdk_json_file)
if time.time() - last_refresh > 3600:
public.run_thread(public.downloadFile, ('{}/src/jdk/jdk.json'.format(public.get_url()), jdk_json_file))
public.writeFile(tip_file, str(int(time.time())))
try:
versions = json.loads(versions_data)
except Exception:
versions = {
"x64": [
"jdk1.7.0_80", "jdk1.8.0_371", "jdk-9.0.4", "jdk-10.0.2",
"jdk-11.0.19", "jdk-12.0.2", "jdk-13.0.2", "jdk-14.0.2",
"jdk-15.0.2", "jdk-16.0.2", "jdk-17.0.8", "jdk-18.0.2.1",
"jdk-19.0.2", "jdk-20.0.2"
],
"arm": [
"jdk1.8.0_371", "jdk-11.0.19", "jdk-15.0.2", "jdk-16.0.2",
"jdk-17.0.8", "jdk-18.0.2.1", "jdk-19.0.2", "jdk-20.0.2"
],
"loongarch64": [
"jdk-8.1.18", "jdk-11.0.22", "jdk-17.0.10", "jdk-21.0.2"
]
}
arch = platform.machine()
if arch == "aarch64" or 'arm' in arch:
arch = "arm"
elif arch == "loongarch64":
arch = "loongarch64"
elif arch == "x86_64":
arch = "x64"
self._versions_list = versions.get(arch, [])
return self._versions_list
def jdk_list_path(self) -> List[str]:
return ["{}/{}".format(self._jdk_path, i) for i in self.versions_list]
@property
def custom_jdk_list(self) -> List[str]:
if self._custom_jdk_list:
return self._custom_jdk_list
try:
self._custom_jdk_list = json.loads(public.readFile(self._custom_file))
except:
self._custom_jdk_list = []
if not isinstance(self._custom_jdk_list, list):
self._custom_jdk_list = []
return self._custom_jdk_list
def add_custom_jdk(self, jdk_path: str) -> Optional[str]:
jdk_path = normalize_jdk_path(jdk_path)
if not jdk_path:
return "jdk路径错误或无法识别"
if jdk_path in self.custom_jdk_list or jdk_path in self.jdk_list_path:
return
self.custom_jdk_list.append(jdk_path)
public.writeFile(self._custom_file, json.dumps(self.custom_jdk_list))
def remove_custom_jdk(self, jdk_path: str) -> None:
if jdk_path not in self.custom_jdk_list:
return
self.custom_jdk_list.remove(jdk_path)
public.writeFile(self._custom_file, json.dumps(self.custom_jdk_list))
def async_install_jdk(self, version: str) -> None:
sh_str = "cd /www/server/panel/install && /bin/bash install_soft.sh {} install {} {}".format(0, 'jdk', version)
if not os.path.exists("/tmp/panelTask.pl"): # 如果当前任务队列并未执行,就把日志清空
public.writeFile('/tmp/panelExec.log', '')
task_id = public.M('tasks').add(
'id,name,type,status,addtime,execstr',
(None, '安装[{}]'.format(version), 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), sh_str))
self._create_install_wait_msg(task_id, version)
@staticmethod
def _create_install_wait_msg(task_id: int, version: str):
from panel_msg.msg_file import message_mgr
file_path = "/tmp/panelExec.log"
if not os.path.exists(file_path):
public.writeFile(file_path, "")
data = {
"soft_name": version,
"install_status": "等待安装" + version,
"file_name": file_path,
"self_type": "soft_install",
"status": 0,
"task_id": task_id
}
title = "等待安装" + version
res = message_mgr.collect_message(title, ["Java环境管理", version], data)
if isinstance(res, str):
public.WriteLog("消息盒子", "安装信息收集失败")
return None
return res
def install_jdk(self, version: str) -> Optional[str]:
if version not in self.versions_list:
return "Version does not exist and cannot be installed"
if os.path.exists(self._jdk_path + "/" + version):
return "已存在的版本, 无法再次安装,如需再次安装请先卸载"
if os.path.exists("{}/{}.pl".format(self._jdk_path, version)):
return "安装任务进行中,请勿再次添加"
public.writeFile("{}/{}.pl".format(self._jdk_path, version), "installing")
t = threading.Thread(target=self._install_jdk, args=(version,))
t.start()
return None
def _install_jdk(self, version: str) -> None:
try:
log_file = "{}/{}_install.log".format(self._jdk_path, version)
if not os.path.exists('/www/server/panel/install/jdk.sh'):
public.ExecShell('wget -O /www/server/panel/install/jdk.sh ' + public.get_url() + '/install/0/jdk.sh')
public.ExecShell('bash /www/server/panel/install/jdk.sh install {} 2>&1 > {}'.format(version, log_file))
except:
pass
public.ExecShell('rm -rf /www/server/java/{}.*'.format(version))
def uninstall_jdk(self, version: str) -> Optional[str]:
if not os.path.exists(self._jdk_path + "/" + version):
return "没有安装指定的版本,无法卸载"
public.ExecShell('rm -rf /www/server/java/{}*'.format(version))
return
@staticmethod
def set_jdk_env(jdk_path) -> Optional[str]:
if jdk_path != "":
jdk_path = normalize_jdk_path(jdk_path)
if not jdk_path:
return "jdk路径错误或无法识别"
# 写入全局的shell配置文件
profile_path = '/etc/profile'
java_home_line = "export JAVA_HOME={}".format(jdk_path) if jdk_path else ""
path_line = "export PATH=$JAVA_HOME/bin:$PATH"
profile_data = public.readFile(profile_path)
if not isinstance(profile_data, str):
return "无法读取环境变量文件"
rep_java_home = re.compile(r"export\s+JAVA_HOME=.*\n")
rep_path = re.compile(r"export\s+PATH=\$JAVA_HOME/bin:\$PATH\s*?\n")
if rep_java_home.search(profile_data):
profile_data = rep_java_home.sub(java_home_line, profile_data)
elif jdk_path:
profile_data = profile_data + "\n" + java_home_line
if rep_path.search(profile_data):
if not jdk_path:
profile_data = rep_path.sub("", profile_data)
elif jdk_path:
profile_data = profile_data + "\n" + path_line
try:
with open(profile_path, "w") as f:
f.write(profile_data)
except PermissionError:
return "无法修改环境变量,可能是系统加固插件拒绝了操作"
except:
return "修改失败"
return
@staticmethod
def get_env_jdk() -> Optional[str]:
profile_data = public.readFile('/etc/profile')
if not isinstance(profile_data, str):
return None
current_java_home = None
for line in profile_data.split("\n"):
if 'export JAVA_HOME=' in line:
current_java_home = line.split('=')[1].strip().replace('"', '').replace("'", "")
return current_java_home
def jps() -> List[int]:
dir_list = [i for i in os.listdir("/tmp") if i.startswith("hsperfdata_")]
return [int(j) for j in itertools.chain(*[os.listdir("/tmp/" + i) for i in dir_list]) if j.isdecimal()]
def js_value_to_bool(value: Any) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() in ("true", "yes", "1")
return bool(value)
def check_port_with_net_connections(port: int) -> bool:
try:
for conn in psutil.net_connections():
if conn.status == 'LISTEN' and conn.laddr.port == port:
return False
except:
pass
return True
def check_port(port) -> bool:
"""
返回false表示端口不可用
"""
if not isinstance(port, int):
port = int(port)
if port == 0:
return False
if not 0 < port < 65535:
return False
project_list = public.M('sites').field('name,path,project_config').select()
for project_find in project_list:
try:
project_config = json.loads(project_find['project_config'])
except json.JSONDecodeError:
continue
if 'port' not in project_config:
continue
if int(project_config['port']) == port:
return False
try:
for conn in psutil.net_connections():
if conn.status == 'LISTEN' and conn.laddr.port == port:
return False
except:
pass
return True
def pass_dir_for_user(path_dir: str, user: str):
"""
给某个用户,对应目录的执行权限
"""
import stat
if not os.path.isdir(path_dir):
return
try:
import pwd
uid_data = pwd.getpwnam(user)
uid = uid_data.pw_uid
gid = uid_data.pw_gid
except:
return
if uid == 0:
return
if path_dir[:-1] == "/":
path_dir = path_dir[:-1]
while path_dir != "/":
path_dir_stat = os.stat(path_dir)
if path_dir_stat.st_uid != uid or path_dir_stat.st_gid != gid:
old_mod = stat.S_IMODE(path_dir_stat.st_mode)
if not old_mod & 1:
os.chmod(path_dir, old_mod+1)
path_dir = os.path.dirname(path_dir)
def create_a_not_used_port() -> int:
"""
生成一个可用的端口
"""
import random
while True:
port = random.randint(2000, 65535)
if check_port_with_net_connections(port):
return port
# 记录项目是通过用户停止的
def stop_by_user(project_id):
file_path = "{}/data/push/tips/project_stop.json".format(public.get_panel_path())
if not os.path.exists(file_path):
data = {}
else:
data_content = public.readFile(file_path)
try:
data = json.loads(data_content)
except json.JSONDecodeError:
data = {}
data[str(project_id)] = True
public.writeFile(file_path, json.dumps(data))
# 记录项目是通过用户操作启动的
def start_by_user(project_id):
file_path = "{}/data/push/tips/project_stop.json".format(public.get_panel_path())
if not os.path.exists(file_path):
data = {}
else:
data_content = public.readFile(file_path)
try:
data = json.loads(data_content)
except json.JSONDecodeError:
data = {}
data[str(project_id)] = False
public.writeFile(file_path, json.dumps(data))
def is_stop_by_user(project_id):
file_path = "{}/data/push/tips/project_stop.json".format(public.get_panel_path())
if not os.path.exists(file_path):
data = {}
else:
data_content = public.readFile(file_path)
try:
data = json.loads(data_content)
except json.JSONDecodeError:
data = {}
if str(project_id) not in data:
return False
return data[str(project_id)]
# # 内置项目复制Tomcat
# def check_and_copy_tomcat(version: int):
# old_path = "/usr/local/bttomcat/tomcat_bak%d"
# new_path = "/usr/local/bt_mod_tomcat/tomcat%d"
# if not os.path.exists("/usr/local/bt_mod_tomcat"):
# os.makedirs("/usr/local/bt_mod_tomcat", 0o755)
#
# src_path = old_path % version
# if not os.path.exists(old_path % version) or not os.path.isfile(src_path + '/conf/server.xml'):
# return
# if os.path.exists(new_path % version):
# return
# else:
# os.makedirs(new_path % version)
#
# public.ExecShell('cp -r %s/* %s ' % (src_path, new_path % version,))
# t = bt_tomcat(version)
# if t:
# t.reset_tomcat_server_config(8330 + version - 6)
# def tomcat_install_status() -> List[dict]:
# res_list = []
# install_path = "/usr/local/bttomcat/tomcat_bak%d"
# for i in range(7, 11):
# src_path = install_path % i
# start_path = src_path + '/bin/daemon.sh'
# conf_path = src_path + '/conf/server.xml'
# if os.path.exists(src_path) and os.path.isfile(start_path) and os.path.isfile(conf_path):
# res_list.append({"version": i, "installed": True})
# else:
# res_list.append({"version": i, "installed": False})
# return res_list