1
0
mirror of https://github.com/0O0o0oOoO00/Alas.git synced 2026-05-14 17:59:25 +08:00
Files
Alas/module/instance_watcher.py

148 lines
5.0 KiB
Python
Raw Permalink Normal View History

import datetime
2025-10-11 18:43:55 +08:00
import pathlib
2025-09-13 14:41:52 +08:00
import threading
import time
from dataclasses import dataclass
from typing import Dict
from module.config.config import AzurLaneConfig
from module.counter import MaxCounter
from module.notify import handle_notify
2025-10-11 18:43:55 +08:00
from module.submodule.utils import has_config_mod
2025-09-13 14:41:52 +08:00
from module.webui.process_manager import ProcessManager
@dataclass
class InstanceSetting:
counter: MaxCounter
enable_notify: bool
notify_config: str
class InstanceWatcher:
2025-10-11 18:43:55 +08:00
STATUS_DIR = "./bin/status"
2025-09-13 14:41:52 +08:00
instance = None
def __init__(self):
self.watcher: threading.Thread = None
self.instances: Dict[str, InstanceSetting] = dict()
self.rest_delta = datetime.timedelta(hours=24)
self.reset_time = datetime.datetime.now() + self.rest_delta
2025-09-13 14:41:52 +08:00
def start(self):
if self.watcher is not None:
if self.watcher.is_alive():
return
self.watcher = threading.Thread(target=self.watcher_thread, daemon=True)
self.watcher.start()
def check_counter(self):
now = datetime.datetime.now()
if now > self.reset_time:
for instance in self.instances.values():
instance.counter.reset()
self.reset_time = now + self.rest_delta
2025-09-13 14:41:52 +08:00
def check_instances(self):
ins_has_triggered = []
ins_inexists = []
2025-09-13 14:41:52 +08:00
for name, setting in self.instances.items():
if not has_config_mod(name):
ins_inexists.append(name)
continue
2025-09-13 14:41:52 +08:00
ins = ProcessManager.get_manager(name)
if not ins.alive:
2025-09-15 17:30:22 +08:00
if ins.state != 3:
continue
2025-09-13 14:41:52 +08:00
if setting.counter.count_once(throw=False):
ins.start("alas")
if setting.enable_notify:
handle_notify(
setting.notify_config,
title=f"Alas <{ins.config_name}> instance auto restarted",
content=f"Critical error occurred, instance restarted",
)
else:
ins_has_triggered.append(name)
handle_notify(
setting.notify_config,
title=f"Alas <{ins.config_name}> instance restarted too many times",
content=f"Too many critical error occurred, instance restarted too many times",
)
for i in ins_inexists:
self.remove_instance(i)
2025-09-13 14:41:52 +08:00
for i in ins_has_triggered:
self.remove_instance(i)
2025-10-11 18:43:55 +08:00
def add_status_file(self, name):
f = pathlib.Path(InstanceWatcher.STATUS_DIR) / f"{name}.status"
if not f.exists():
f.parent.mkdir(parents=True, exist_ok=True)
f.touch()
def remove_status_file(self, name):
f = pathlib.Path(InstanceWatcher.STATUS_DIR) / f"{name}.status"
if f.exists():
f.unlink()
def get_all_prev_instance(self):
f = pathlib.Path(InstanceWatcher.STATUS_DIR)
if not f.exists():
f.mkdir(parents=True, exist_ok=True)
return [i.stem for i in f.glob("*.status")]
def has_status_file(self, name):
f = pathlib.Path(InstanceWatcher.STATUS_DIR) / f"{name}.status"
return f.exists()
def try_add_instance(self, name, add_status_cache_file=True):
if name in self.instances:
return
2025-09-13 14:41:52 +08:00
config = AzurLaneConfig(name)
full_config = config.full_config
if full_config.Restart_InstanceRestart_Enable:
setting = InstanceSetting(
counter=MaxCounter(full_config.Restart_InstanceRestart_MaxRetryTimes),
enable_notify=full_config.Restart_InstanceRestart_Notify,
notify_config=full_config.Alas_Error_OnePushConfig
)
self.instances[name] = setting
if add_status_cache_file:
self.add_status_file(name)
2025-10-11 18:43:55 +08:00
def check_instance_status(self):
for config_name in self.get_all_prev_instance():
if not has_config_mod(config_name):
self.remove_status_file(config_name)
2025-10-11 18:43:55 +08:00
continue
ins = ProcessManager.get_manager(config_name)
if ins.alive:
continue
if ins.detailed_instance_status == ProcessManager.DetailInstanceStatus.NoRenderables:
ins.start("alas")
2025-09-13 14:41:52 +08:00
def remove_instance(self, name, remove_status_cache_file=True):
2025-09-13 14:41:52 +08:00
try:
self.instances.pop(name)
except Exception:
...
if remove_status_cache_file:
self.remove_status_file(name)
2025-09-13 14:41:52 +08:00
def watcher_thread(self):
while 1:
time.sleep(60)
try:
self.check_counter()
2025-09-13 14:41:52 +08:00
self.check_instances()
2025-10-11 18:43:55 +08:00
self.check_instance_status()
2025-09-13 14:41:52 +08:00
except Exception:
...
@staticmethod
def get_instance() -> "InstanceWatcher":
if InstanceWatcher.instance is None:
InstanceWatcher.instance = InstanceWatcher()
return InstanceWatcher.instance