#!/usr/bin/env python3
import time, logging, subprocess, urllib.request, json, ssl, socket, datetime, os, hashlib
logging.basicConfig(level=logging.INFO, format='%(asctime)s [MetaWorker] %(message)s')
log = logging.getLogger(__name__)
NODE_ID = "NODE-HW-VLRWX9-001"
INTERVAL = 60
GATEWAY = 'https://www.huodouai.com'
DID = 'DID-BR-000002'

# 性能基线：自动学习每个API的正常响应时间
PERF_BASELINE = {}
# 配置基线哈希
CONFIG_BASELINE = {}
# 自愈历史记录
HEAL_HISTORY = []

SERVICES = [
    ('nginx', 'Nginx', 'systemctl restart nginx', 'http://127.0.0.1:80/', 100),
    ('a2a_gateway.py', 'A2A网关', 'pkill -f a2a_gateway.py; sleep 1; cd /www/wwwroot/huodouai.com/a2a && nohup python3 a2a_gateway.py &', 'http://127.0.0.1:8930/health', 100),
    ('octop run', 'Octop主服务', 'systemctl restart octop', 'http://127.0.0.1:8088/', 500),
]

def sh(cmd):
    try: return subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, timeout=10).decode().strip()
    except: return ''

def perf_test(url):
    # 测API响应时间，和基线对比
    t0 = time.time()
    try:
        urllib.request.urlopen(url, timeout=5)
        ms = (time.time() - t0) * 1000
        return ms
    except: return -1

def learn_baseline(url, ms):
    # 前10次采样学习基线，之后偏离2倍告警
    if url not in PERF_BASELINE:
        PERF_BASELINE[url] = []
    PERF_BASELINE[url].append(ms)
    if len(PERF_BASELINE[url]) > 10:
        PERF_BASELINE[url] = PERF_BASELINE[url][-10:]

def perf_degraded(url, ms):
    if url not in PERF_BASELINE or len(PERF_BASELINE[url]) < 5:
        return False  # 还在学习期
    avg = sum(PERF_BASELINE[url]) / len(PERF_BASELINE[url])
    return ms > avg * 2 and avg > 0

def config_fingerprint(path):
    # 配置文件指纹哈希，检测配置漂移
    try:
        with open(path) as f:
            return hashlib.md5(f.read().encode()).hexdigest()
    except: return ''

def detect_config_drift():
    # 对比nginx配置当前指纹和基线
    nginx_conf = '/usr/local/nginx/conf/nginx.conf'
    fp = config_fingerprint(nginx_conf)
    if nginx_conf not in CONFIG_BASELINE:
        CONFIG_BASELINE[nginx_conf] = fp
        return False
    return CONFIG_BASELINE[nginx_conf] != fp

def predict_oom(proc_mem, limit):
    # 简单线性预测：按当前增长速度，预测多久后OOM
    if proc_mem > limit * 0.8:
        return True  # 已经快到阈值了
    return False

def track_heal(svc, action):
    HEAL_HISTORY.append({'svc': svc, 'action': action, 'ts': time.time()})
    if len(HEAL_HISTORY) > 20:
        HEAL_HISTORY.pop(0)

def heal_success_rate():
    if not HEAL_HISTORY: return 100
    return len([h for h in HEAL_HISTORY if h['action'] == 'restart']) / len(HEAL_HISTORY) * 100

def main():
    log.info(f'Meta-Worker V5.0 智能运维版启动 {NODE_ID}')
    log.info('高阶能力: 性能基线学习/配置漂移检测/OOM预测/自愈成功率统计')
    daily = False
    while True:
        try:
            events = []
            for proc, svc, restart, health_url, mem_limit in SERVICES:
                # 基础存活
                if int(sh(f'pgrep -f "{proc}" | wc -l')) == 0:
                    log.warning(f'{svc} 进程消失，自动重启')
                    sh(restart)
                    track_heal(svc, 'restart')
                    events.append(f'{svc}重启')
                    continue
                # 性能基线学习
                ms = perf_test(health_url)
                if ms > 0:
                    learn_baseline(health_url, ms)
                    if perf_degraded(health_url, ms):
                        log.warning(f'{svc} 性能退化: {ms:.0f}ms (基线{sum(PERF_BASELINE[health_url])/len(PERF_BASELINE[health_url]):.0f}ms)')
                        events.append(f'{svc}性能退化({ms:.0f}ms)')
            # 每日深度智能检测
            if not daily:
                # 配置漂移检测
                if detect_config_drift():
                    log.warning('检测到Nginx配置漂移！')
                    events.append('nginx配置被修改')
                # OOM预测
                for proc, svc, restart, health_url, mem_limit in SERVICES:
                    pid = sh(f'pgrep -f "{proc}" | head -1')
                    if pid:
                        mem = float(sh(f'ps -p {pid} -o rss= | awk "{{print \/1024}}"') or 0)
                        if predict_oom(mem, mem_limit):
                            log.warning(f'{svc} 内存{mem:.0f}MB接近阈值{mem_limit}MB，预测OOM风险')
                            events.append(f'{svc}OOM风险({mem:.0f}MB)')
                # 自愈成功率
                rate = heal_success_rate()
                log.info(f'历史自愈成功率: {rate:.0f}% | 累计自愈{len(HEAL_HISTORY)}次')
                daily = True
            log.info(f'巡检完成 事件:{len(events)}')
        except Exception as e:
            log.error(f'异常: {e}')
        time.sleep(INTERVAL)

if __name__ == '__main__':
    main()
