Initial commit: SenSu Alpha 0.2.0
- 13-service async plugin framework - Textual TUI with CLI fallback - Plugin hot-reload + permission system - Web management panel (aiohttp) - Bridge-based inter-module communication - 10 regression tests Fixes applied: - PBKDF2-SHA256 auth (was plain SHA256) - Auth bypass removed (was allow-all on fail) - Bare excepts replaced with logged errors - CatFramework/DreamSu -> SenSu naming unified - ServiceManager: health checks + startup_order - Env var credentials (SENSU_ADMIN_PASSWORD etc)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import time
|
||||
import platform
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def _is_android() -> bool:
|
||||
"""检测是否为 Android 环境 (Termux 等)"""
|
||||
return (
|
||||
'ANDROID_ROOT' in os.environ or
|
||||
os.path.exists('/system/bin/getprop') or
|
||||
platform.release().lower().find('android') != -1
|
||||
)
|
||||
|
||||
|
||||
class SystemInfoCollector:
|
||||
def __init__(self):
|
||||
self.is_android = 'ANDROID_ROOT' in os.environ or os.path.exists('/system/bin/getprop')
|
||||
self.psutil = None
|
||||
if not self.is_android:
|
||||
try:
|
||||
import psutil
|
||||
self.psutil = psutil
|
||||
except ImportError as e:
|
||||
logger.debug(f"psutil not available: {e}")
|
||||
else:
|
||||
logger.info("🤖 Android 平台识别,启用原生采集")
|
||||
|
||||
def get_all(self):
|
||||
return {
|
||||
"platform": {
|
||||
"system": platform.system(),
|
||||
"machine": platform.machine(),
|
||||
"python": platform.python_version()
|
||||
},
|
||||
"cpu": self._get_cpu(),
|
||||
"memory": self._get_memory(),
|
||||
"network": self._get_network()
|
||||
}
|
||||
|
||||
def _get_cpu(self):
|
||||
if self.psutil:
|
||||
return {
|
||||
"percent": self.psutil.cpu_percent(interval=0.1),
|
||||
"cores": self.psutil.cpu_count(),
|
||||
"load_avg": os.getloadavg() if hasattr(os, 'getloadavg') else [0,0,0]
|
||||
}
|
||||
# Android 估算:负载率 = (1分钟负载 / 核心数) * 100
|
||||
try:
|
||||
load = os.getloadavg()
|
||||
cores = os.cpu_count() or 1
|
||||
percent = min(100.0, (load[0] / cores) * 100)
|
||||
return {"percent": round(percent, 1), "cores": cores, "load_avg": load}
|
||||
except Exception as e:
|
||||
logger.error(f"System info collection error: {e}", exc_info=True)
|
||||
return {"percent": 0, "cores": 0, "load_avg": [0,0,0]}
|
||||
|
||||
def _get_memory(self):
|
||||
if self.psutil:
|
||||
m = self.psutil.virtual_memory()
|
||||
return {"total_gb": round(m.total/1073741824, 1), "used_gb": round(m.used/1073741824, 1), "percent": m.percent}
|
||||
try:
|
||||
mem = {}
|
||||
with open('/proc/meminfo') as f:
|
||||
for line in f:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2: mem[parts[0].rstrip(':')] = int(parts[1]) * 1024
|
||||
t, a = mem.get('MemTotal', 1), mem.get('MemAvailable', mem.get('MemFree', 0))
|
||||
return {"total_gb": round(t/1073741824, 1), "used_gb": round((t-a)/1073741824, 1), "percent": round(((t-a)/t)*100, 1)}
|
||||
except Exception as e:
|
||||
logger.warning(f"Memory info failed: {e}")
|
||||
return {"total_gb": 0, "used_gb": 0, "percent": 0}
|
||||
|
||||
def _get_network(self):
|
||||
if self.psutil:
|
||||
io = self.psutil.net_io_counters()
|
||||
return {"rx": round(io.bytes_recv/1048576, 1), "tx": round(io.bytes_sent/1048576, 1)}
|
||||
# Android 解析 /proc/net/dev
|
||||
try:
|
||||
rx = 0
|
||||
with open('/proc/net/dev', 'r') as f:
|
||||
for line in f:
|
||||
if ':' in line and 'lo' not in line: # 排除 lo 回环
|
||||
parts = line.split(':')[1].split()
|
||||
rx += int(parts[0]) # RX bytes
|
||||
return {"rx": round(rx/1048576, 1), "tx": 0}
|
||||
except Exception as e:
|
||||
logger.warning(f"Network info failed: {e}")
|
||||
return {"rx": 0, "tx": 0}
|
||||
Reference in New Issue
Block a user