| |
| |
|
|
| import os |
| import re |
| import shutil |
| import sys |
| from typing import List, Dict |
|
|
| from .config import NGINX_CONF_DIR |
| from .nginx_utils import NginxUtils |
| from mod.project.node.dbutil import LoadSite, HttpNode, TcpNode, ServerNodeDB |
| from typing import Union |
|
|
| from ..nodeutil import ServerNode |
|
|
| if "/www/server/panel/class" not in sys.path: |
| sys.path.insert(0, "/www/server/panel/class") |
|
|
| import public |
|
|
|
|
| class NginxConfigGenerator: |
| def __init__(self): |
| self.nginx_conf_dir = NGINX_CONF_DIR |
| self.nginx_tcp_conf_dir = os.path.join(self.nginx_conf_dir, "tcp") |
| self.nginx_proxy_conf_dir = os.path.join(self.nginx_conf_dir, "proxy") |
|
|
| def generate_http_config(self, load: LoadSite, nodes: List[HttpNode]) -> Dict[str, str]: |
| """生成HTTP负载均衡配置""" |
| |
| upstream_conf = self._generate_http_upstream_config(load, nodes) |
|
|
| |
| server_conf = self._generate_server_config(load.site_name) |
|
|
| |
| proxy_conf = self._generate_proxy_config(load, nodes) |
|
|
| return { |
| 'upstream': upstream_conf, |
| 'server': server_conf, |
| 'proxy': proxy_conf |
| } |
|
|
| @staticmethod |
| def _generate_http_upstream_config(load: LoadSite, nodes: List[HttpNode]) -> str: |
| """生成upstream配置""" |
| config = "upstream {} {{\n".format(load.name) |
|
|
| |
| if load.http_config["http_alg"] == "sticky_cookie": |
| config += " sticky name=bt_load_srv_id expires=12h;\n" |
| elif load.http_config["http_alg"] == "ip_hash": |
| config += " ip_hash;\n" |
| elif load.http_config["http_alg"] == "least_conn": |
| config += " least_conn;\n" |
|
|
| can_use_backup = not load.http_config["http_alg"] in ["hash", "ip_hash", "random", "sticky_cookie"] |
|
|
| srv_db = ServerNodeDB() |
| |
| for node in nodes: |
| server = "{}:{}".format(node.node_site_name, node.port) |
| if srv_db.is_local_node(node.node_id): |
| server = "127.0.0.1:" + str(node.port) |
| elif load.site_name == node.node_site_name: |
| server_ip = ServerNode.get_node_ip(node.node_id) |
| if server_ip: |
| server = server_ip + ":" + str(node.port) |
| backup_str = " backup" if node.node_status == "backup" and can_use_backup else "" |
| backup_str = " down" if node.node_status == "down" else backup_str |
| config += " server {} weight={} max_fails={} fail_timeout={} {};\n".format( |
| server, node.weight, node.max_fail, |
| node.fail_timeout, backup_str, |
| ) |
| config += "}\n" |
| return config |
|
|
| @staticmethod |
| def _generate_server_config(site_name: str) -> str: |
| """生成server配置""" |
| site_ng_file = "/www/server/panel/vhost/nginx/{}.conf".format(site_name) |
| ng_conf = public.readFile(site_ng_file) |
| rep = re.compile(r"include.*/vhost/nginx/proxy/.*\*.conf;") |
| if not rep.search(ng_conf): |
| ng_proxy_file_str = "/www/server/panel/vhost/nginx/proxy/{}/*.conf;".format(site_name) |
| replace_str = "\n\t#引用反向代理规则,注释后配置的反向代理将无效\n\tinclude {}\n\n\tinclude enable-php-".format( |
| ng_proxy_file_str) |
| ng_conf = ng_conf.replace("include enable-php-", replace_str) |
| ng_conf = re.sub(r"location .{3,10}\((gif|js)\|[^{]*[^}]*}\n", "", ng_conf) |
| return ng_conf |
|
|
| @staticmethod |
| def _generate_proxy_config(load: LoadSite, nodes: List[HttpNode]) -> str: |
| """生成proxy配置""" |
| is_https = False |
| for node in nodes: |
| if node.port in (443, 8443): |
| is_https = True |
| break |
|
|
| if not os.path.exists("/www/wwwlogs/load_balancing/logs/" + load.site_name): |
| os.makedirs("/www/wwwlogs/load_balancing/logs/" + load.site_name) |
|
|
| proxy_cache = "" |
| if load.http_config.get("proxy_cache_status", False): |
| cache_suffix = load.http_config.get("cache_suffix", "") |
| if cache_suffix: |
| parameter = r".*\.(" + "|".join([re.escape(i) for i in cache_suffix.split(",")]) + ")$" |
| else: |
| parameter = None |
| cache_time = load.http_config.get("cache_time", "1d") |
| if not re.match(r"^[0-9]+([smhd])$", cache_time): |
| cache_time = "1d" |
| if parameter: |
| proxy_cache = """ |
| add_header X-Cache $upstream_cache_status; |
| location ~ {parameter} {{ |
| proxy_pass {is_https}://{name}; |
| proxy_cache cache_one; |
| proxy_cache_key $host$uri$is_args$args; |
| proxy_ignore_headers Set-Cookie Cache-Control expires X-Accel-Expires; |
| proxy_cache_valid 200 304 301 302 {cache_time}; |
| proxy_cache_valid 404 {cache_time}; |
| }} |
| """.format(is_https="https" if is_https else "http", |
| name=load.name, |
| cache_time=cache_time, |
| parameter=parameter) |
|
|
| config = """location / {{ |
| proxy_pass {is_https}://{name}; |
| |
| # 基本代理设置 |
| proxy_set_header Host $host; |
| proxy_set_header X-Real-IP $remote_addr; |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| proxy_set_header X-Forwarded-Proto $scheme; |
| # 禁用缓存 |
| proxy_cache off; |
| # 错误处理 |
| proxy_next_upstream {next_upstream};{proxy_cache} |
| }} |
| |
| # 使用详细日志格式 |
| access_log /www/wwwlogs/load_balancing/logs/{site_name}/proxy_access.log load_balancer_detailed; |
| """.format( |
| site_name=load.site_name, |
| name=load.name, |
| is_https="https" if is_https else "http", |
| next_upstream=load.http_config["proxy_next_upstream"], |
| proxy_cache=proxy_cache, |
| ) |
| return config |
|
|
| @staticmethod |
| def generate_tcp_config(load: LoadSite, nodes: List[TcpNode]) -> str: |
| """生成TCP配置""" |
| tcp_upstream = "" |
| for node in nodes: |
| backup_str = " backup" if node.node_status == "backup" else "" |
| backup_str = " down" if node.node_status == "down" else backup_str |
| tcp_upstream += " server {}:{} weight={} max_fails={} fail_timeout={}{};\n".format( |
| node.host, node.port, node.weight, node.max_fail, node.fail_timeout, backup_str |
| ) |
| os.makedirs("/www/wwwlogs/load_balancing/tcp_logs/" + load.name, exist_ok=True) |
| tcp_config = load.tcp_config |
| config = """upstream %s { |
| %s |
| } |
| |
| server { |
| listen %s:%d%s; |
| proxy_connect_timeout %ds; |
| proxy_timeout %ds; |
| proxy_pass %s; |
| access_log /www/wwwlogs/load_balancing/tcp_logs/%s/tcp_load.log load_balancer_tcp_detailed; |
| } |
| """ % (load.name, tcp_upstream, tcp_config["host"], tcp_config["port"], "" if tcp_config["type"] == "tcp" else " udp", |
| tcp_config["proxy_connect_timeout"], tcp_config["proxy_timeout"], load.name, load.name) |
| return config |
|
|
| def save_configs(self, load: LoadSite, nodes: List[Union[TcpNode, HttpNode]]) -> str: |
| """保存配置文件""" |
| if load.site_type == "http": |
| NginxUtils.set_load_balancer_log_format() |
| |
| configs = self.generate_http_config(load, nodes) |
| site_name = load.site_name |
| upstream_back, server_back, proxy_back = "", "", "" |
| upstream_path = os.path.join(self.nginx_conf_dir, "upstream_{}.conf".format(site_name)) |
| if os.path.exists(upstream_path): |
| upstream_back = public.readFile(upstream_path) |
| public.writeFile(upstream_path, configs['upstream']) |
|
|
| |
| server_path = os.path.join(self.nginx_conf_dir, "{}.conf".format(site_name)) |
| if os.path.exists(server_path): |
| server_back = public.readFile(server_path) |
| public.writeFile(server_path, configs['server']) |
|
|
| |
| proxy_dir = os.path.join(self.nginx_proxy_conf_dir, site_name) |
| os.makedirs(proxy_dir, exist_ok=True) |
| proxy_path = os.path.join(proxy_dir, "load_proxy_{}.conf".format(site_name)) |
| if os.path.exists(proxy_path): |
| proxy_back = public.readFile(proxy_path) |
| public.writeFile(proxy_path, configs['proxy']) |
|
|
| flag, err = NginxUtils.check_config() |
| if not flag: |
| public.writeFile(upstream_path, upstream_back) |
| public.writeFile(server_path, server_back) |
| public.writeFile(proxy_path, proxy_back) |
| return err |
| else: |
| NginxUtils.reload_nginx() |
|
|
| elif load.site_type == "tcp": |
| NginxUtils.set_tcp_load_balancer_log_format() |
| |
| tcp_dir = self.nginx_tcp_conf_dir |
| os.makedirs(tcp_dir, exist_ok=True) |
|
|
| site_name = load.name |
| conf = self.generate_tcp_config(load, nodes) |
| stream_back = "" |
| stream_path = os.path.join(tcp_dir, "{}.conf".format(site_name)) |
| if os.path.exists(stream_path): |
| stream_back = public.readFile(stream_path) |
| public.writeFile(stream_path, conf) |
| flag, err = NginxUtils.check_config() |
| if not flag: |
| public.writeFile(stream_path, stream_back) |
| return err |
| else: |
| NginxUtils.reload_nginx() |
|
|
| def set_http_proxy_next_upstream(self, site_name: str, proxy_next_upstream: str) -> str: |
| proxy_path = os.path.join(self.nginx_proxy_conf_dir, site_name, "load_proxy_{}.conf".format(site_name)) |
| proxy_back = "" |
| if os.path.exists(proxy_path): |
| proxy_back = public.readFile(proxy_path) |
|
|
| rep = re.compile(r'proxy_next_upstream\s+[^;]*;') |
| proxy_conf = re.sub(rep, 'proxy_next_upstream {};'.format(proxy_next_upstream), proxy_back) |
| if proxy_conf != proxy_back: |
| public.writeFile(proxy_path, proxy_conf) |
| flag, err = NginxUtils.check_config() |
| if not flag: |
| public.writeFile(proxy_path, proxy_back) |
| return err |
| else: |
| NginxUtils.reload_nginx() |
| return "" |
|
|
| def set_http_proxy_cache(self, site_name: str, load: LoadSite, nodes: List[Union[TcpNode, HttpNode]]) -> str: |
| proxy_path = os.path.join(self.nginx_proxy_conf_dir, site_name, "load_proxy_{}.conf".format(site_name)) |
| proxy_back = "" |
| if os.path.exists(proxy_path): |
| proxy_back = public.readFile(proxy_path) |
|
|
| proxy_conf = self._generate_proxy_config(load, nodes) |
| if proxy_conf != proxy_back: |
| public.writeFile(proxy_path, proxy_conf) |
| flag, err = NginxUtils.check_config() |
| if not flag: |
| public.writeFile(proxy_path, proxy_back) |
| return err |
| else: |
| NginxUtils.reload_nginx() |
| return "" |
|
|
|
|
| def delete_node_conf(self, load: LoadSite, mutil=False): |
| if load.site_type == "http": |
| site_name = load.site_name |
| upstream_path = os.path.join(self.nginx_conf_dir, "upstream_{}.conf".format(site_name)) |
| if os.path.exists(upstream_path): |
| os.remove(upstream_path) |
| proxy_path = os.path.join(self.nginx_proxy_conf_dir, site_name, "load_proxy_{}.conf".format(site_name)) |
| if os.path.exists(proxy_path): |
| os.remove(proxy_path) |
|
|
| log_dir = os.path.join("/www/wwwlogs/load_balancing/logs/", site_name) |
| if os.path.exists(log_dir): |
| shutil.rmtree(log_dir) |
|
|
| log_cache_dir = os.path.join("/www/wwwlogs/load_balancing/cache/", site_name) |
| if os.path.exists(log_cache_dir): |
| shutil.rmtree(log_cache_dir) |
| else: |
| site_name = load.name |
| tcp_conf = os.path.join(self.nginx_tcp_conf_dir, "{}.conf".format(site_name)) |
| if os.path.exists(tcp_conf): |
| os.remove(tcp_conf) |
|
|
| log_dir = os.path.join("/www/wwwlogs/load_balancing/tcp_logs/", site_name) |
| if os.path.exists(log_dir): |
| shutil.rmtree(log_dir) |
|
|
| log_cache_dir = os.path.join("/www/wwwlogs/load_balancing/cache/", site_name) |
| if os.path.exists(log_cache_dir): |
| shutil.rmtree(log_cache_dir) |
| if not mutil: |
| NginxUtils.reload_nginx() |
|
|