#!/usr/bin/env python3 """ ServerGuard - 服务器硬件健康诊断系统 主程序入口,负责命令行参数解析、模块调度和报告生成。 使用方法: sudo python3 main.py --quick # 快速检测 sudo python3 main.py --full # 全面诊断(含压力测试) sudo python3 main.py --module cpu # 仅检测 CPU sudo python3 main.py --full --format json --output report.json """ import argparse import sys import os from typing import Optional, Dict, Any from utils import setup_logging, check_root_privileges, get_file_timestamp from reporter import ReportGenerator def parse_arguments() -> argparse.Namespace: """ 解析命令行参数。 Returns: argparse.Namespace: 解析后的参数 """ parser = argparse.ArgumentParser( prog='ServerGuard', description='服务器硬件健康诊断系统 - 用于诊断 Linux 服务器硬件故障', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: %(prog)s --quick # 快速硬件检测 %(prog)s --full # 全面诊断(含压力测试) %(prog)s --module cpu # 仅检测 CPU %(prog)s --module memory,storage # 检测内存和存储 %(prog)s --full --format json # 生成 JSON 格式报告 %(prog)s --list-modules # 列出所有可用模块 注意: 大多数诊断功能需要 root 权限,请使用 sudo 运行。 """ ) # 主要操作模式(互斥) mode_group = parser.add_mutually_exclusive_group(required=True) mode_group.add_argument( '--quick', '-q', action='store_true', help='快速检测模式(非侵入性,仅收集信息)' ) mode_group.add_argument( '--full', '-f', action='store_true', help='全面诊断模式(包含压力测试,耗时较长)' ) mode_group.add_argument( '--module', '-m', type=str, metavar='MODULE', help='运行指定模块,多个模块用逗号分隔 (cpu,memory,storage,sensors,gpu,logs)' ) mode_group.add_argument( '--list-modules', '-l', action='store_true', help='列出所有可用的检测模块' ) # 报告选项 parser.add_argument( '--format', type=str, choices=['text', 'json', 'csv', 'html'], default='text', help='报告格式 (默认: text)' ) parser.add_argument( '--output', '-o', type=str, metavar='FILE', help='输出文件路径(不指定则输出到控制台)' ) parser.add_argument( '--log', type=str, metavar='FILE', default='/var/log/serverguard.log', help='日志文件路径 (默认: /var/log/serverguard.log)' ) # 测试参数 parser.add_argument( '--stress-duration', type=int, default=300, metavar='SECONDS', help='压力测试持续时间,单位秒 (默认: 300)' ) parser.add_argument( '--verbose', '-v', action='store_true', help='显示详细输出' ) parser.add_argument( '--yes', '-y', action='store_true', help='自动确认所有警告提示(如压力测试警告)' ) return parser.parse_args() def list_available_modules(): """列出所有可用的检测模块。""" modules = { 'system': '系统信息概览', 'cpu': 'CPU 检测与压力测试', 'memory': '内存检测与压力测试', 'storage': '存储设备检测', 'sensors': '电源与传感器监控', 'gpu': '显卡检测', 'logs': '日志分析' } print("可用的检测模块:") print("-" * 40) for name, description in modules.items(): print(f" {name:12} - {description}") print("-" * 40) print("\n使用示例:") print(" sudo python3 main.py --module cpu") print(" sudo python3 main.py --module cpu,memory,storage") def confirm_stress_test(duration: int, auto_confirm: bool = False) -> bool: """ 确认是否执行压力测试。 Args: duration: 压力测试持续时间 auto_confirm: 是否自动确认 Returns: bool: 是否继续 """ if auto_confirm: return True print("\n" + "=" * 60) print("警告:即将执行压力测试") print("=" * 60) print(f"测试持续时间: {duration} 秒 ({duration // 60} 分钟)") print("此测试将占用大量系统资源,可能导致:") print(" - CPU 和内存使用率接近 100%") print(" - 系统响应变慢") print(" - 温度升高") print("建议在维护窗口期进行,并确保服务器可接受高负载。") print("=" * 60) try: response = input("\n是否继续? [y/N]: ").strip().lower() return response in ('y', 'yes') except KeyboardInterrupt: print("\n操作已取消") return False def run_module(module_name: str, stress_test: bool = False, stress_duration: int = 300) -> Dict[str, Any]: """ 运行指定的检测模块。 Args: module_name: 模块名称 stress_test: 是否执行压力测试 stress_duration: 压力测试持续时间 Returns: Dict[str, Any]: 模块检测结果 """ import logging logger = logging.getLogger(__name__) module_map = { 'system': 'modules.system_info', 'cpu': 'modules.cpu', 'memory': 'modules.memory', 'storage': 'modules.storage', 'sensors': 'modules.sensors', 'gpu': 'modules.gpu', 'logs': 'modules.log_analyzer' } if module_name not in module_map: logger.error(f"未知模块: {module_name}") return {"status": "error", "error": f"未知模块: {module_name}"} try: module = __import__(module_map[module_name], fromlist=['']) if module_name == 'system': return module.get_system_info() elif module_name == 'cpu': return module.run_cpu_check(stress_test, stress_duration) elif module_name == 'memory': return module.run_memory_check(stress_test, stress_duration) elif module_name == 'storage': return module.run_storage_check() elif module_name == 'sensors': return module.run_sensors_check() elif module_name == 'gpu': return module.run_gpu_check() elif module_name == 'logs': return module.analyze_logs() except Exception as e: logger.error(f"运行模块 {module_name} 时出错: {e}") return {"status": "error", "error": str(e)} def run_quick_check() -> Dict[str, Any]: """ 执行快速检测(非侵入性)。 Returns: Dict[str, Any]: 检测结果 """ import logging logger = logging.getLogger(__name__) print("正在执行快速硬件检测...") print("-" * 60) results = { "scan_type": "quick", "timestamp": get_file_timestamp(), "modules": {} } modules_to_run = ['system', 'cpu', 'memory', 'storage', 'sensors', 'gpu', 'logs'] for module_name in modules_to_run: print(f"正在检测: {module_name}...", end=' ', flush=True) try: result = run_module(module_name, stress_test=False) results["modules"][module_name] = result status = result.get("status", "unknown") if status == "success": print("[完成]") elif status == "warning": print("[警告]") elif status == "error": print("[错误]") else: print(f"[{status}]") except Exception as e: logger.error(f"模块 {module_name} 执行失败: {e}") results["modules"][module_name] = {"status": "error", "error": str(e)} print("[失败]") print("-" * 60) return results def run_full_diagnostic(stress_duration: int, auto_confirm: bool = False) -> Dict[str, Any]: """ 执行全面诊断(包含压力测试)。 Args: stress_duration: 压力测试持续时间 auto_confirm: 是否自动确认 Returns: Dict[str, Any]: 检测结果 """ import logging logger = logging.getLogger(__name__) if not confirm_stress_test(stress_duration, auto_confirm): print("诊断已取消") sys.exit(0) print("\n正在执行全面硬件诊断...") print("=" * 60) results = { "scan_type": "full", "timestamp": get_file_timestamp(), "stress_duration": stress_duration, "modules": {} } # 先执行快速检测 modules_to_run = ['system', 'cpu', 'memory', 'storage', 'sensors', 'gpu', 'logs'] for module_name in modules_to_run: print(f"\n正在检测: {module_name}...") try: # CPU 和内存执行压力测试 do_stress = module_name in ['cpu', 'memory'] result = run_module(module_name, stress_test=do_stress, stress_duration=stress_duration) results["modules"][module_name] = result status = result.get("status", "unknown") print(f" 状态: {status}") except Exception as e: logger.error(f"模块 {module_name} 执行失败: {e}") results["modules"][module_name] = {"status": "error", "error": str(e)} print(f" 状态: 失败 - {e}") print("\n" + "=" * 60) return results def run_specific_modules(module_list: str, stress_duration: int) -> Dict[str, Any]: """ 运行指定的模块列表。 Args: module_list: 逗号分隔的模块名称 stress_duration: 压力测试持续时间 Returns: Dict[str, Any]: 检测结果 """ modules = [m.strip() for m in module_list.split(',')] results = { "scan_type": "custom", "timestamp": get_file_timestamp(), "modules": {} } print(f"正在执行自定义模块检测: {', '.join(modules)}") print("-" * 60) for module_name in modules: print(f"正在检测: {module_name}...", end=' ', flush=True) try: result = run_module(module_name, stress_test=False) results["modules"][module_name] = result status = result.get("status", "unknown") print(f"[{status}]") except Exception as e: results["modules"][module_name] = {"status": "error", "error": str(e)} print(f"[失败: {e}]") print("-" * 60) return results def main(): """程序主入口。""" args = parse_arguments() # 设置日志 log_level = logging.DEBUG if args.verbose else logging.INFO setup_logging( log_file=args.log if check_root_privileges() else None, level=log_level, console_output=True ) logger = logging.getLogger(__name__) # 列出模块 if args.list_modules: list_available_modules() sys.exit(0) # 检查 root 权限警告 if not check_root_privileges(): logger.warning("未以 root 权限运行,部分功能可能受限") print("警告: 未检测到 root 权限,部分硬件信息可能无法获取") print("建议: 使用 sudo 运行以获得完整的诊断信息\n") # 执行诊断 try: if args.quick: results = run_quick_check() elif args.full: results = run_full_diagnostic(args.stress_duration, args.yes) elif args.module: results = run_specific_modules(args.module, args.stress_duration) else: print("请指定操作模式: --quick, --full, --module 或 --list-modules") sys.exit(1) # 生成报告 generator = ReportGenerator() if args.output: generator.save_report(results, args.format, args.output) print(f"\n报告已保存至: {args.output}") else: report = generator.generate_report(results, args.format) print("\n" + "=" * 60) print("诊断报告") print("=" * 60) print(report) # 返回退出码:如果有错误则返回 1 has_errors = any( m.get("status") == "error" for m in results.get("modules", {}).values() ) sys.exit(1 if has_errors else 0) except KeyboardInterrupt: print("\n\n操作已被用户中断") sys.exit(130) except Exception as e: logger.exception("程序执行过程中发生错误") print(f"\n错误: {e}") sys.exit(1) if __name__ == '__main__': import logging main()