Initial YakPanel commit
This commit is contained in:
0
mod/project/java/__init__.py
Normal file
0
mod/project/java/__init__.py
Normal file
1169
mod/project/java/groupMod.py
Normal file
1169
mod/project/java/groupMod.py
Normal file
File diff suppressed because it is too large
Load Diff
34
mod/project/java/group_script.py
Normal file
34
mod/project/java/group_script.py
Normal 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)
|
||||
|
||||
|
||||
575
mod/project/java/java_web_conf.py
Normal file
575
mod/project/java/java_web_conf.py
Normal 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设置静态路由"
|
||||
BIN
mod/project/java/jmxquery/JMXQuery-0.1.8.jar
Normal file
BIN
mod/project/java/jmxquery/JMXQuery-0.1.8.jar
Normal file
Binary file not shown.
215
mod/project/java/jmxquery/__init__.py
Normal file
215
mod/project/java/jmxquery/__init__.py
Normal 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)
|
||||
3252
mod/project/java/projectMod.py
Normal file
3252
mod/project/java/projectMod.py
Normal file
File diff suppressed because it is too large
Load Diff
600
mod/project/java/project_update.py
Normal file
600
mod/project/java/project_update.py
Normal 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]))
|
||||
|
||||
644
mod/project/java/server_proxy.py
Normal file
644
mod/project/java/server_proxy.py
Normal 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()
|
||||
|
||||
|
||||
1047
mod/project/java/springboot_parser.py
Normal file
1047
mod/project/java/springboot_parser.py
Normal file
File diff suppressed because it is too large
Load Diff
946
mod/project/java/utils.py
Normal file
946
mod/project/java/utils.py
Normal 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 "%r" %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
|
||||
|
||||
Reference in New Issue
Block a user