import copy import json import os.path import sys import time import psutil from typing import Optional, Dict, Union, List, Tuple, Any, Iterable if "/www/server/panel/class" not in sys.path: sys.path.insert(0, "/www/server/panel/class") import public from mod.base import RealServer, json_response from mod.project.java import utils from mod.project.java.projectMod import debug, main as JavaProject class Group: GROUP_DATA_DIR = "/www/server/panel/data/java_group" GROUP_TMP_DIR = "/var/tmp/springboot/group" def __init__(self, group_id: Optional[str] = None, group_data: Optional[dict] = None): self.group_id = group_id if isinstance(group_data, dict): self.config: Optional[Dict[str, Union[str, List]]] = group_data else: self.config = self.load_group_data_by_id() if not os.path.exists(self.GROUP_DATA_DIR): os.makedirs(self.GROUP_DATA_DIR, 0o600) if not os.path.exists(self.GROUP_TMP_DIR): os.makedirs(self.GROUP_TMP_DIR) self.running_data = { "length": 0, # 已完成的数量 "remaining": 0, # 未完成的数量 "executing": 0, # 正在执行的数量 "projects": [], # 项目操作详情 "msg": "", # 信息 "status": True, } # 如果有配置文件,则表示可以执行 def can_running(self) -> bool: return isinstance(self.config, dict) @staticmethod def new_group_id() -> str: from uuid import uuid4 return uuid4().hex[::2] # 从配置文件中加载数据 def load_group_data_by_id(self) -> Optional[dict]: config_file = "{}/{}.json".format(self.GROUP_DATA_DIR, self.group_id) try: data = json.loads(public.readFile(config_file)) except: return None if isinstance(data, dict): return data else: return None def save_group_data(self): if self.config: config_file = "{}/{}.json".format(self.GROUP_DATA_DIR, self.group_id) public.writeFile(config_file, json.dumps(self.config)) # 更新旧版数据 @classmethod def update_group_data(cls) -> None: json_file = "/www/server/panel/class/projectModel/java_project_groups.json" if not os.path.isfile(json_file): return data_str = public.readFile(json_file) try: data = json.loads(data_str) except: os.remove(json_file) return try: java_projects = public.M("sites").where('project_type=?', ('Java',)).field("id,name").select() except: return projects_dict = {i["name"]: i["id"] for i in java_projects} for idx, i in enumerate(data): name = i.get("group_name", "默认分组-{}".format(idx + 1)) projects = i.get("projects", []) order = i.get("order", []) # type: list tmp_p = [] for p in projects: if p["project_name"] in projects_dict and p["project_name"] in order: tmp_p.append({ "id": projects_dict[p["project_name"]], "name": p["project_name"], "level": order.index(p["project_name"]) + 1, "check_info": { "type": "port", "port": [], "wait_time": 180, } }) if tmp_p: group_id = cls.new_group_id() public.writeFile( "{}/{}.json".format(cls.GROUP_DATA_DIR, group_id), json.dumps({ "group_name": name, "projects": tmp_p, "sort_type": "sequence", }) ) if os.path.isfile(json_file): os.remove(json_file) # 检查数据 def check_group_data(self) -> Optional[str]: """ 一个组的格式 group = { "group_name": "aaa", "projects": [ { "id": 84, "name": "tduck-api", "level": 1, "check_info": { "type": ("port" or "active"), "port": [8456, 8511], "wait_time": 180, } } ], "sort_type": ("simultaneous" or "sequence") } """ # 检查self.config是否为字典 if not isinstance(self.config, dict): return "Parameter format error" # 检查'sort_type'键是否存在且值为("simultaneous" or "sequence") if "sort_type" not in self.config or self.config["sort_type"] not in ("simultaneous", "sequence"): return "编排方式设置错误" if 'group_name' not in self.config or not isinstance(self.config["group_name"], str) \ or not self.config["group_name"]: return "分组名称设置错误" # 检查projects键是否存在且为列表 if "projects" not in self.config or not isinstance(self.config["projects"], list): return "项目列表设置错误" # 遍历projects列表中的每个项目 for project in self.config["projects"]: # 检查每个项目是否为字典 if not isinstance(project, dict): return "项目列表设置错误" # 检查'id', 'name', 'level', 'check_info'键是否存在 if not all(key in project for key in ("id", "name", "level", "check_info")): return "项目项目配置信息缺失" # 检查id、level是否为整数 if not isinstance(project["id"], int) or not isinstance(project["level"], int): return "The Project ID or Priority parameter is in the wrong format" # 检查'check_info'是否为字典 if not isinstance(project["check_info"], dict): return "项目{}检查策略设置错误".format(project["name"]) # 检查'check_info'中的type, 'port', 'wait_time' if not all(key in project["check_info"] for key in ("type", "port", "wait_time")): return "项目{}检查策略信息缺失".format(project["name"]) # 检查type的值 if project["check_info"]["type"] not in ("port", "active"): return "项目{}检查策略类型设置错误".format(project["name"]) # 如果所有检查都通过,则数据格式正确 return None # 执行 check_info 中的判断,返回现在的进程是否属于在运行中, 如果不是运行中, 就是异常 @staticmethod def do_check_info(check_info: dict, process: psutil.Process): if check_info["type"] == "port": timeout = time.time() > process.create_time() + 60 * 3 listen = [] connections = process.connections() for connection in connections: if connection.status == "LISTEN": listen.append(connection.laddr.port) if not check_info["port"]: if not timeout and not bool(listen): return "waiting" if not bool(listen): return 'failed' else: return 'succeeded' else: res = not bool(set(check_info["port"]) - set(listen)) # 如果所有的端口都在监听中,则返回True if not timeout and not res: return 'waiting' if res: return 'succeeded' else: return 'failed' else: create_time = process.create_time() if time.time() > create_time + int(check_info["wait_time"]): return "succeeded" else: return "waiting" @staticmethod def is_running(pid: int): try: return psutil.Process(pid).is_running() except: return False # 获取运行状态信息 def run_status(self, last_write_time: float) -> dict: default_error = { "running": False, "msg": "操作出错,已退出", "running_data": None, "last_write_time": 0, } pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) log_file = "{}/{}.log".format(Group.GROUP_TMP_DIR, self.group_id) if not os.path.isfile(pid_file): return default_error try: pid = int(public.readFile(pid_file)) except: return default_error try: data = json.loads(public.readFile(log_file)) except: # 如果读取出错,则说明进程出现问题,则杀死进程,并清除数据,返回错误 if os.path.isfile(log_file): os.remove(log_file) if os.path.isfile(pid_file): os.remove(pid_file) if self.is_running(pid): psutil.Process(pid).kill() return default_error # 如果进程不在运行,则返回上一次运行的数据(防止卡顿导致导致最后的数据没有读取到) if not self.is_running(pid): return { "running": False, "msg": "操作进程已退出", "running_data": data, "last_write_time": os.path.getmtime(log_file), } # 如果还在运行, 则进行长链接, 等待最多5秒, 如果没有数据被写入, 且进程依旧在运行,则返回上一次的数据; # 若不在运行则说明,且没有写入则说明等待期间出错了 (启动进程退出前必定会进行一次写入) # 如果有数据被写入则返回最新数据 for i in range(50): m_time = os.path.getmtime(log_file) if abs(m_time - last_write_time) > 0.001: # 如果时间差大于0.001秒,则说明数据再次被写入了,则退出循环,返回数据 now_write_time = m_time break elif i == 49: # 最后一次检测后,直接退出循环 continue else: time.sleep(0.1) else: # 如果 5 秒内没有数据被写入,则返回上一次的数据 if not self.is_running(pid): # 不在运行, 说明等待期间出错了 if os.path.isfile(log_file): os.remove(log_file) if os.path.isfile(pid_file): os.remove(pid_file) return default_error # 在运行,则返回上一次的数据 return { "running": self.is_running(pid), "msg": "运行中", "running_data": data, "last_write_time": last_write_time, } # try: data = json.loads(public.readFile(log_file)) except: if self.is_running(pid): psutil.Process(pid).kill() if os.path.isfile(log_file): os.remove(log_file) if os.path.isfile(pid_file): os.remove(pid_file) return { "running": False, "msg": "操作进程出错,已退出", "running_data": None, "last_write_time": 0, } running = self.is_running(pid) return { "running": running, "msg": "运行中" if running else "操作进程已结束", "running_data": data, "last_write_time": now_write_time, } # 获取可操作状态的信息 def get_operation_info(self) -> Tuple[Iterable[str], str]: pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) try: pid = int(public.readFile(pid_file)) p = psutil.Process(pid) if not p.is_running(): return ("start", "stop"), "" else: last_cmd = p.cmdline()[-2] if len(p.cmdline()) == 4 else "" return ("termination",), last_cmd except: pass return ("start", "stop"), "" def group_info(self, project_cache: Optional[dict] = None) -> Optional[dict]: if not self.config: return if not project_cache: project_ids = [i["id"] for i in self.config["projects"]] if not project_ids: data = copy.deepcopy(self.config) data["group_id"] = self.group_id return data projects = public.M('sites').where( 'project_type=? and id IN ({})'.format(",".join(["?"] * len(project_ids))), ('Java', *project_ids)).select() project_cache = {} for i in projects: i["project_config"] = json.loads(i["project_config"]) project_cache[i["id"]] = i j_pro = JavaProject() res = copy.deepcopy(self.config) res["group_id"] = self.group_id res["operation_info"], res["now_operation"] = self.get_operation_info() running_num = 0 for i in res["projects"]: if i["id"] in project_cache: try: listen = [] pid = j_pro.get_project_pid(project_cache[i["id"]]) p = psutil.Process(int(pid)) connections = p.connections() for connection in connections: if connection.status == "LISTEN": listen.append(connection.laddr.port) except: i["running"] = False i["project_status"] = 'not_run' i["pid"] = None i["listen"] = [] continue running_num += 1 i["running"] = True i["project_status"] = self.do_check_info(i["check_info"], p) i["pid"] = p.pid i["listen"] = listen if not res["now_operation"]: all_num = len(res["projects"]) if all_num == 0: res["operation_info"] = [] else: if running_num == all_num: res["operation_info"] = ["stop"] if running_num == 0: res["operation_info"] = ["start"] return res # 运行一个Group相关的函数 # ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ def run_operation(self, operation: str) -> Optional[str]: pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) if operation not in ("start", "stop"): return "指定的操作不存在" try: p = psutil.Process(public.readFile(pid_file)) if p.is_running(): return "操作运行中, 请等待上一个操作执行完成" except: pass if os.path.exists(pid_file): os.remove(pid_file) self.build_run_sort(reverse=(operation == "stop")) self.save_running_data() panel_path = "/www/server/panel" public.ExecShell( "nohup {}/pyenv/bin/python3 {}/mod/project/java/group_script.py {} {} &> /tmp/group_script.log & \n" "echo $! > {} ".format( panel_path, panel_path, operation, self.group_id, pid_file) ) return None def build_run_sort(self, reverse=False) -> List[List[dict]]: projects = self.config["projects"] # type: List[Dict[str, Union[int, str, dict]]] if self.config["sort_type"] == "simultaneous": projects_group = [projects] else: projects_group = [] projects.sort(key=lambda x: x['level']) last_level = -1 for i in projects: if i['level'] != last_level: last_level = i['level'] projects_group.append([i]) else: projects_group[-1].append(i) self.running_data['length'] = len(projects) self.running_data['remaining'] = len(projects) for g in projects_group: names = [i["name"] for i in g] self.running_data["projects"].append({ "names": names, "msg": "", "running": False, "data": { i["name"]: {"status": False, "msg": "", "pid": 0, "name": i["name"]} for i in g } }) if reverse: self.running_data["projects"] = self.running_data["projects"][::-1] return projects_group[::-1] return projects_group # 真正执行启动的逻辑 def real_run_start(self): pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) pid = os.getpid() public.writeFile(pid_file, str(pid)) if not self.can_running(): self.running_data["msg"] = "项目组配置文件错误,无法启动该项目组" self.running_data["status"] = False self.save_running_data() return # 构建运行启动的信息,并返回运行顺序 self.running_data["msg"] = "启动操作运行中" run_sort = self.build_run_sort(reverse=False) self.save_running_data() for idx, g in enumerate(run_sort): res_msg = self._run_start_one_step(g, sort_idx=idx) if isinstance(res_msg, str): self.running_data["msg"] = res_msg self.running_data["status"] = False self.save_running_data() return self.running_data["msg"] = "启动操作运行成功" self.running_data["status"] = True self.save_running_data() def save_running_data(self): log_file = "{}/{}.log".format(Group.GROUP_TMP_DIR, self.group_id) public.writeFile(log_file, json.dumps(self.running_data)) # 执行启动任务的每一个优先级中所有的项目 def _run_start_one_step(self, projects: List[dict], sort_idx: int) -> Optional[str]: j_pro = JavaProject() step_running_data = self.running_data["projects"][sort_idx] # type: Dict[str, Any] step_running_data["running"] = True step_running_data["msg"] = "正在启动【{}】项目".format(", ".join(step_running_data["names"])) self.running_data["remaining"] -= len(projects) self.running_data["executing"] = len(projects) self.save_running_data() need_wait_project = [] error_msg = None # 尝试启动每一个项目 for p in projects: project_data = j_pro.get_project_find(p["name"]) if not project_data: step_running_data["data"][p["name"]]["msg"] = "项目【{}】已丢失,无法启动".format(p["name"]) continue project_pid = j_pro.get_project_pid(project_data) if project_pid: step_running_data["data"][p["name"]]["status"] = True step_running_data["data"][p["name"]]["msg"] = "项目【{}】已处于运行状态,未重新执行启动操作".format( p["name"]) step_running_data["data"][p["name"]]["pid"] = project_pid continue res = j_pro.start_spring_boot_project(project_data, wait=False) # 不等待启动成功 if not res["status"]: step_running_data["data"][p["name"]]["status"] = False step_running_data["data"][p["name"]]["msg"] = "在执行启动项目【{}】指令的时出现错误:{}".format( p["name"], res["msg"]) error_msg = "出现无法处理的项目启动问题:" + res["msg"] break # 出现无法启动的项目,直接退出 else: need_wait_project.append((project_data, p["check_info"])) if error_msg: return error_msg if not need_wait_project: return None self.save_running_data() time.sleep(0.5) while need_wait_project: remove_idx = [] error_projects = [] change = False for idx, (pd, check_info) in enumerate(need_wait_project): if "process" not in pd or not isinstance(pd["process"], psutil.Process): try: pid = j_pro.get_project_pid(pd) process = psutil.Process(int(pid)) except: process = None else: process = pd["process"] if not isinstance(process, psutil.Process) or not process.is_running(): step_running_data["data"][pd["name"]]["status"] = False step_running_data["data"][pd["name"]]["msg"] = "项目【{}】启动失败,详细情况请查看日志".format( pd["name"]) change = True remove_idx.append(idx) error_projects.append(pd["name"]) continue else: if step_running_data["data"][pd["name"]]["pid"] == 0: step_running_data["data"][pd["name"]]["pid"] = process.pid change = True tmp_res = self.do_check_info(check_info, process) if tmp_res == "succeeded": step_running_data["data"][pd["name"]]["status"] = True step_running_data["data"][pd["name"]]["msg"] = "项目【{}】启动成功".format(pd["name"]) remove_idx.append(idx) self.running_data["executing"] -= 1 change = True elif tmp_res == "failed": step_running_data["data"][pd["name"]]["status"] = False step_running_data["data"][pd["name"]]["msg"] = "项目【{}】启动失败,详细情况请查看日志".format( pd["name"]) remove_idx.append(idx) error_projects.append(pd["name"]) change = True continue if remove_idx: for idx in remove_idx: need_wait_project.pop(idx) if error_projects: return "项目【{}】启动失败无法继续执行启动操作,请查看日志".format(",".join(error_projects)) if change is True: self.save_running_data() if need_wait_project: time.sleep(0.2) step_running_data["running"] = False self.save_running_data() return def real_run_stop(self): pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) pid = os.getpid() public.writeFile(pid_file, str(pid)) if not self.can_running(): self.running_data["msg"] = "项目组配置文件错误,无法停止该项目组" self.running_data["status"] = False self.save_running_data() return # 构建运行启动的信息,并返回运行顺序 self.running_data["msg"] = "项目组停止操作运行中" run_sort = self.build_run_sort(reverse=True) self.save_running_data() for idx, g in enumerate(run_sort): self._run_stop_one_step(g, sort_idx=idx) self.running_data["msg"] = "停止操作运行成功" self.running_data["status"] = True self.save_running_data() def _run_stop_one_step(self, projects: List[dict], sort_idx: int) -> None: j_pro = JavaProject() step_running_data = self.running_data["projects"][sort_idx] # type: Dict[str, Any] step_running_data["running"] = True step_running_data["msg"] = "正在执行【{}】项目的停止任务".format(", ".join(step_running_data["names"])) self.running_data["remaining"] -= len(projects) self.running_data["executing"] = len(projects) need_wait_project = [] # 尝试停止每一个项目 for p in projects: project_data = j_pro.get_project_find(p["name"]) if not project_data: step_running_data["data"][p["name"]]["msg"] = "项目【】已丢失,无法执行停止操作" continue project_pid = j_pro.get_project_pid(project_data) if not project_pid: step_running_data["data"][p["name"]]["status"] = True step_running_data["data"][p["name"]]["msg"] = "项目【{}】已停止".format(p["name"]) continue else: project_data["pid"] = project_pid project_config = project_data["project_config"] server_name = "spring_" + project_config["project_name"] + project_config.get("server_name_suffix", "") s_admin = RealServer() if s_admin.daemon_status(server_name)["msg"] == "服务不存在!": j_pro.stop_by_kill_pid(project_data) if os.path.isfile(project_config["pids"]): os.remove(project_config["pids"]) else: s_admin.daemon_admin(server_name, "stop") utils.stop_by_user(project_data["id"]) need_wait_project.append((project_data, p["check_info"])) if not need_wait_project: return None self.save_running_data() time.sleep(0.05) wait_num = 0 while need_wait_project and wait_num < 10: remove_idx = [] change = False for idx, (pd, check_info) in enumerate(need_wait_project): pid = pd["pid"] pid_info = j_pro.real_process.get_process_info_by_pid(pid)["data"] if not pid_info: step_running_data["data"][pd["name"]]["status"] = True step_running_data["data"][pd["name"]]["msg"] = "项目【{}】停止成功".format(pd["name"]) remove_idx.append(idx) self.running_data["executing"] -= 1 change = True if remove_idx: for idx in remove_idx: need_wait_project.pop(idx) if change is True: self.save_running_data() wait_num += 1 if need_wait_project: time.sleep(0.1) step_running_data["running"] = False self.save_running_data() return def termination_operation(self) -> None: pid_file = "{}/{}.pid".format(Group.GROUP_TMP_DIR, self.group_id) try: pid = int(public.readFile(pid_file)) p = psutil.Process(pid) p.kill() os.remove(pid_file) except: pass class GroupMager: cache_type = ["springboot"] def __init__(self): self.project_cache: Optional[dict] = None if not os.path.exists(Group.GROUP_DATA_DIR): os.makedirs(Group.GROUP_DATA_DIR, 0o600) if not os.path.exists(Group.GROUP_TMP_DIR): os.makedirs(Group.GROUP_TMP_DIR) def _get_project_cache(self): if self.project_cache is not None: return java_projects = public.M("sites").where('project_type=?', ('Java',)).select() _cache = {} for i in java_projects: config = json.loads(i["project_config"]) if config["java_type"] in self.cache_type: i["project_config"] = config _cache[i["id"]] = i self.project_cache = _cache def group_list(self) -> List[dict]: group_list = [] for i in os.listdir(Group.GROUP_DATA_DIR): file = "{}/{}".format(Group.GROUP_DATA_DIR, i) if os.path.isfile(file) and i.endswith(".json"): group_id = i[:-5] try: data = json.loads(public.readFile(file)) except: continue g = Group(group_id, data) group_list.append(g) if not group_list: return [] self._get_project_cache() res = [] for i in group_list: res.append(i.group_info(self.project_cache)) return res @staticmethod def add_group(data: dict) -> Optional[str]: g = Group(Group.new_group_id(), data) res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def remove_group(group_id: str) -> None: config_file = "{}/{}.json".format(Group.GROUP_DATA_DIR, group_id) if os.path.isfile(config_file): os.remove(config_file) @staticmethod def add_project_to_group(group_id: str, project_name: str, check_info: dict, level: Optional[int]) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" p = public.M("sites").where('project_type=? AND name=?', ('Java', project_name)).find() if not p: return "指定项目【{}】不存在".format(project_name) project_config = json.loads(p["project_config"]) if not project_config["java_type"] in GroupMager.cache_type: return "指定项目【{}】不是spring boot项目, 不支持添加到项目组".format(project_name) used_project = [p["id"] for p in g.config["projects"]] if p["id"] in used_project: return "项目【{}】已存在于项目组【{}】".format(project_name, group_id) if level is None: if not g.config["projects"]: level = 1 else: level = max([p["level"] for p in g.config["projects"]]) + 1 g.config["projects"].append({ "name": project_name, "id": p["id"], "check_info": check_info, "level": level, }) res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def add_projects_to_group(group_id: str, project_ids: List[int]) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" project_list = public.M("sites").where( 'project_type=? AND id IN ({})'.format(",".join(["?"] * len(project_ids))), ('Java', *project_ids) ).select() used_project = [p["id"] for p in g.config["projects"]] start_level = max([p["level"] for p in g.config["projects"]] + [1]) # 最小值为1 for p in project_list: if p["id"] in used_project: return "项目【{}】已存在于项目组【{}】".format(p["name"], group_id) project_config = json.loads(p["project_config"]) if not project_config["java_type"] in GroupMager.cache_type: return "指定项目【{}】不是spring boot项目, 不支持添加到项目组".format(p["name"]) g.config["projects"].append({ "name": p["name"], "id": p["id"], "check_info": { "type": "port", "port": [], "wait_time": 180, }, "level": start_level + 1, }) start_level += 1 res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def remove_project_from_group(group_id: str, project_id: int) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" target_idx = None for idx, p in enumerate(g.config["projects"]): if p["id"] == project_id: target_idx = idx break if target_idx is not None: g.config["projects"].pop(target_idx) g.save_group_data() @staticmethod def modify_group(group_id: str, group_data: dict) -> Optional[str]: g = Group(group_id, group_data) res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def modify_group_projects(group_id: str, project_datas: list) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" project_ids = [p["id"] for p in project_datas] project_list = public.M("sites").where( 'project_type=? AND id IN ({})'.format(",".join(["?"] * len(project_ids))), ('Java', *project_ids) ).select() for p in project_list: project_config = json.loads(p["project_config"]) if not project_config["java_type"] in GroupMager.cache_type: return "指定项目【{}】不是spring boot项目, 不支持设置到项目组".format(p["name"]) g.config["projects"] = project_datas res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def modify_group_project(group_id: str, project_id: int, check_info: Optional[dict], level: Optional[int]) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" p = public.M("sites").where('project_type=? AND id=?', ('Java', project_id)).find() if not p: return "The specified item does not exist" target_idx = None for idx, p in enumerate(g.config["projects"]): if p["id"] == project_id: target_idx = idx break if target_idx is None: return "指定项目不在该项目组内" update_data = {} if check_info is not None: update_data["check_info"] = check_info if level is not None: update_data["level"] = level g.config["projects"][target_idx].update(update_data) res = g.check_group_data() if isinstance(res, str): return res g.save_group_data() @staticmethod def change_sort_type(group_id: str, sort_type: str) -> Optional[str]: g = Group(group_id) if not g.config: return "项目组不存在" if sort_type not in ("simultaneous", "sequence"): return "排序方式错误" g.config["sort_type"] = sort_type g.save_group_data() Group.update_group_data() class main: def __init__(self): pass @staticmethod def group_list(get): return GroupMager().group_list() @staticmethod def group_info(get): try: group_id = get.group_id.strip() except: return json_response(status=False, msg="Parameter error") g = Group(group_id) if not g.config: return json_response(status=False, msg="项目组不存在") else: return json_response(status=True, data=g.group_info()) @staticmethod def add_group(get): try: group_name = get.group_name.strip() except: return json_response(status=False, msg="Parameter error") res = GroupMager().add_group({ "group_name": group_name, "sort_type": "simultaneous", "projects": [], }) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="添加成功") @staticmethod def remove_group(get): try: group_id = get.group_id.strip() except: return json_response(status=False, msg="Parameter error") GroupMager.remove_group(group_id) return json_response(status=True, msg="Successfully delete") @staticmethod def add_project_to_group(get): check_info = { "type": "port", "port": [], "wait_time": 180, } level = None try: group_id = get.group_id.strip() project_name = get.project_name.strip() if hasattr(get, "check_info") and get.check_info: check_info = json.loads(get.check_info) if hasattr(get, "level") and get.level: level = int(get.level) except: return json_response(status=False, msg="Parameter error") res = GroupMager.add_project_to_group(group_id, project_name, check_info, level) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="添加成功") @staticmethod def add_projects_to_group(get): try: group_id = get.group_id.strip() if hasattr(get, "project_ids") and isinstance(get.project_ids, str): project_ids = json.loads(get.project_ids) else: if isinstance(get.project_ids, list): project_ids = get.project_ids else: return json_response(status=False, msg="Parameter error") except: return json_response(status=False, msg="Parameter error") res = GroupMager.add_projects_to_group(group_id, project_ids) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="添加成功") @staticmethod def remove_project_from_group(get): try: group_id = get.group_id.strip() project_id = int(get.project_id) except: return json_response(status=False, msg="Parameter error") res = GroupMager.remove_project_from_group(group_id, project_id) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="Successfully delete") @staticmethod def modify_group(get): try: group_id = get.group_id.strip() if isinstance(get.group_data, str): group_data = json.loads(get.group_data) else: group_data = get.group_data except: return json_response(status=False, msg="Parameter error") res = GroupMager.modify_group(group_id, group_data) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="修改成功") @staticmethod def modify_group_project(get): check_info = None level = None try: group_id = get.group_id.strip() project_id = int(get.project_id) if hasattr(get, "check_info") and get.check_info: check_info = json.loads(get.check_info) if hasattr(get, "level") and get.level: level = int(get.level) except: return json_response(status=False, msg="Parameter error") res = GroupMager.modify_group_project(group_id, project_id, check_info, level) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="修改成功") @staticmethod def modify_group_projects(get): try: group_id = get.group_id.strip() if isinstance(get.project_datas, str): project_datas = json.loads(get.project_datas) else: project_datas = get.project_datas except: return json_response(status=False, msg="Parameter error") res = GroupMager.modify_group_projects(group_id, project_datas) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="修改成功") @staticmethod def change_sort_type(get): try: group_id = get.group_id.strip() sort_type = get.sort_type.strip() except: return json_response(status=False, msg="Parameter error") res = GroupMager.change_sort_type(group_id, sort_type) if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="修改成功") @staticmethod def start_group(get): try: group_id = get.group_id.strip() except: return json_response(status=False, msg="Parameter error") g = Group(group_id) if not g.config: return json_response(status=False, msg="项目组不存在") res = g.run_operation("start") if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="启动进行中") @staticmethod def stop_group(get): try: group_id = get.group_id.strip() except: return json_response(status=False, msg="Parameter error") g = Group(group_id) if not g.config: return json_response(status=False, msg="项目组不存在") res = g.run_operation("stop") if isinstance(res, str): return json_response(status=False, msg=res) else: return json_response(status=True, msg="停止进行中") @staticmethod def get_run_status(get): last_write_time = 0 try: group_id = get.group_id.strip() if hasattr(get, "last_write_time") and get.last_write_time: last_write_time = float(get.last_write_time) except: return json_response(status=False, msg="Parameter error") g = Group(group_id) if not g.config: return json_response(status=False, msg="项目组不存在") run_status = g.run_status(last_write_time) run_status["running_step"] = None if isinstance(run_status, dict): if isinstance(run_status["running_data"], dict) and "projects" in run_status["running_data"]: projects = run_status["running_data"]["projects"] for i in projects: if i["running"] is True: run_status["running_step"] = i return json_response(status=True, data=run_status) @staticmethod def termination_operation(get): try: group_id = get.group_id.strip() except: return json_response(status=False, msg="Parameter error") Group(group_id).termination_operation() return json_response(status=True, msg="operate successfully") @staticmethod def spring_projects(get): java_projects = public.M("sites").where('project_type=?', ('Java',)).field("name,id,project_config").select() res = [] for i in java_projects: config = json.loads(i["project_config"]) if config["java_type"] in GroupMager.cache_type: res.append({ "name": i["name"], "id": i["id"] }) return res