提交 1e7ae2e8 authored 作者: 陈泽健's avatar 陈泽健

chore: 移除大Excel文件和测试报告历史,加入.gitignore

上级 584cfe65
......@@ -46,6 +46,19 @@ description: ARM集群夜间监控 - 监测9网段四台ARM架构服务器集群
- **必须等待用户确认计划后方可开始执行**,不得跳过确认步骤直接操作;
- 计划执行文档保存到 `.claude/skills/ARM-CLUSTER-MONITOR/计划执行_{YYYYMMDD_HHMMSS}.md`
### 代码实现规范
- **代码存放位置**:如需代码实现(如Python脚本、工具类等),统一存放在 `.claude/skills/ARM-CLUSTER-MONITOR/code/` 目录下;
- **代码文件命名**:使用有意义的文件名,例如:
- `ssh_manager.py` - SSH连接管理工具
- `monitor_agent.py` - 监控Agent实现
- `report_generator.py` - 报告生成工具
- **代码规范**
- 所有代码必须包含中文注释
- 代码风格符合项目规范(参考 `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 使用UTF-8编码
- 函数和类需包含文档字符串说明
- **代码版本管理**:代码文件应纳入Git版本控制,提交信息使用中文描述。
## Usage
```
......
@echo off
cd /d E:\GithubData\ubains-module-test
cd /d E:\GithubData\ubains-module-test\develop
E:\nodejs\claude.cmd --permission-mode bypassPermissions
......@@ -70,7 +70,7 @@ schedule_interval: "2h"
### 0.4 需求文档处理规范
- **每次理解需求文档都需要输出《计划执行》文档**
- **每次理解需求文档,都需要输出《计划执行》文档**
- 计划执行文档需包含:执行目标、执行步骤、预期结果、风险评估
- 用户确认后方可执行
......@@ -96,7 +96,95 @@ schedule_interval: "2h"
| [风险1] | [高/中/低] | [应对方法] |
## 确认信息
请确认是否执行?(是/否)
请确认是否执行?(是/否)
```
### 0.5 代码实现约束
> ⚠️ 如果本 Skill 需要通过代码实现功能,必须遵守以下约束:
#### 代码存放位置规范
**强制要求**: 所有代码文件必须存放在本 Skill 的 `code/` 目录下:
```
.claude/skills/X86-CLUSTER-MONITOR/
├── SKILL.md # Skill说明文档
├── config.json # Skill配置文件
├── 集群信息配置.md # 集群配置信息
└── code/ # 代码实现目录(强制)
├── __init__.py # Python包初始化文件
├── ssh_manager.py # SSH连接管理模块
├── server_checker.py # 服务器检查模块
├── report_generator.py # 报告生成模块
├── utils.py # 工具函数模块
└── main.py # 主执行入口
```
#### 代码规范要点
1. **目录结构**:
- 代码文件必须位于 `code/` 目录内
- 每个模块职责单一,便于维护
- 必须包含 `__init__.py` 文件标识为Python包
2. **代码风格**:
- 所有注释使用中文
- 函数命名遵循 `snake_case` 规范
- 类命名遵循 `PascalCase` 规范
- 每个模块必须有详细的模块级注释
3. **模块划分建议**:
- `ssh_manager.py`: SSH连接管理、免密配置
- `server_checker.py`: 各项服务器检查逻辑
- `report_generator.py`: 报告生成与保存
- `utils.py`: 通用工具函数(时间处理、格式化等)
- `main.py`: 主入口,协调各模块执行
4. **依赖管理**:
-`code/` 目录下创建 `requirements.txt`
- 列出所需Python包及版本
5. **配置文件引用**:
- 读取 `../集群信息配置.md` 获取服务器信息
- 读取 `../config.json` 获取Skill配置
- 配置文件路径使用相对路径
#### 示例代码结构
```python
# ssh_manager.py
"""
SSH连接管理模块
负责SSH连接建立、免密配置、连接池管理
"""
import paramiko
import os
from pathlib import Path
class SSHManager:
"""SSH连接管理器"""
def __init__(self, server_ip):
"""
初始化SSH管理器
Args:
server_ip (str): 服务器IP地址
"""
self.server_ip = server_ip
self.key_path = Path.home() / '.ssh' / 'keys' / server_ip
def connect(self):
"""
建立SSH连接
Returns:
paramiko.SSHClient: SSH客户端对象
"""
# 实现连接逻辑...
pass
```
---
......
"""
X86集群监控代码实现包
提供SSH连接管理、服务器状态检查、报告生成等功能
"""
__version__ = '1.0.0'
__author__ = 'czj'
\ No newline at end of file
"""
X86集群监控主执行模块
协调SSH连接、服务器检查、报告生成
功能:
1. 读取集群配置
2. 并行检查四台服务器
3. 生成监控报告
4. 输出执行摘要
使用方法:
python main.py
"""
import sys
import logging
from pathlib import Path
from datetime import datetime
import concurrent.futures
# 导入其他模块
from ssh_manager import SSHManager, connect_server
from server_checker import ServerChecker
from report_generator import ReportGenerator
from utils import setup_logging, parse_server_config_md, format_timestamp
logger = logging.getLogger(__name__)
class ClusterMonitor:
"""集群监控主控制器"""
def __init__(self, config_path=None):
"""
初始化集群监控器
Args:
config_path (str): 集群配置文件路径
"""
# 配置日志
setup_logging()
# 读取配置
if config_path:
self.config_path = Path(config_path)
else:
# 默认配置文件
self.config_path = Path(__file__).parent.parent / '集群信息配置.md'
# 读取服务器列表
self.servers = parse_server_config_md(self.config_path)
logger.info(f'已加载 {len(self.servers)} 台服务器配置')
def check_server(self, server_info):
"""
检查单台服务器
Args:
server_info (dict): 服务器信息
Returns:
dict: 检查结果
"""
server_ip = server_info['ip']
logger.info(f'开始检查服务器: {server_ip}')
try:
# 连接服务器
ssh_manager = connect_server(
server_ip=server_ip,
username=server_info.get('username', 'root')
)
# 执行检查
checker = ServerChecker(ssh_manager)
result = checker.run_full_check()
# 关闭连接
ssh_manager.close()
logger.info(f'服务器检查完成: {server_ip}')
return result
except Exception as e:
logger.error(f'服务器检查失败: {server_ip}, 错误: {e}')
return {
'server_ip': server_ip,
'error': str(e)
}
def run_cluster_check(self):
"""
执行集群检查(并行)
Returns:
list: 所有服务器检查结果
"""
logger.info('开始执行集群检查...')
results = []
# 并行检查四台服务器
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
future_to_server = {
executor.submit(self.check_server, server): server
for server in self.servers
}
for future in concurrent.futures.as_completed(future_to_server):
server = future_to_server[future]
try:
result = future.result()
results.append(result)
except Exception as e:
logger.error(f'服务器 {server["ip"]} 检查异常: {e}')
results.append({
'server_ip': server['ip'],
'error': str(e)
})
logger.info(f'集群检查完成,共检查 {len(results)} 台服务器')
return results
def generate_report(self, check_results):
"""
生成监控报告
Args:
check_results (list): 检查结果列表
Returns:
str: 报告文件路径
"""
logger.info('开始生成监控报告...')
generator = ReportGenerator()
report_path = generator.generate_report(check_results)
logger.info(f'报告已生成: {report_path}')
return report_path
def print_summary(self, check_results):
"""
输出执行摘要到控制台
Args:
check_results (list): 检查结果列表
"""
print("\n" + "="*60)
print("X86集群监控执行摘要")
print("="*60)
print(f"\n执行时间: {format_timestamp()}")
print(f"监控服务器数: {len(check_results)}")
# 统计状态
normal_count = 0
warning_count = 0
error_count = 0
for result in check_results:
if 'error' in result:
error_count += 1
else:
# 检查资源使用
cpu = float(result['system_resources']['cpu'].replace('%', '').strip())
memory = float(result['system_resources']['memory'].replace('%', '').strip())
if cpu > 90 or memory > 90:
error_count += 1
elif cpu > 80 or memory > 80:
warning_count += 1
else:
normal_count += 1
print(f"\n服务器状态:")
print(f" 🟢 正常: {normal_count}")
print(f" 🟡 警告: {warning_count}")
print(f" 🔴 异常: {error_count}")
# 输出异常服务器
if error_count > 0:
print("\n异常服务器:")
for result in check_results:
if 'error' in result:
print(f" - {result['server_ip']}: {result['error']}")
print("\n" + "="*60)
def run(self):
"""
执行完整监控流程
"""
logger.info('开始执行X86集群监控...')
# 执行检查
check_results = self.run_cluster_check()
# 生成报告
report_path = self.generate_report(check_results)
# 输出摘要
self.print_summary(check_results)
logger.info(f'监控完成,报告路径: {report_path}')
return report_path
def main():
"""主函数"""
try:
# 创建监控器
monitor = ClusterMonitor()
# 执行监控
report_path = monitor.run()
print(f"\n✅ 监控完成")
print(f"📄 报告路径: {report_path}")
return 0
except Exception as e:
logger.error(f'监控执行失败: {e}')
print(f"\n❌ 监控失败: {e}")
return 1
if __name__ == '__main__':
sys.exit(main())
\ No newline at end of file
"""
报告生成模块
负责生成Markdown格式的监控报告
功能:
1. 根据检查数据生成报告
2. 分析集群整体状态
3. 识别异常和告警
4. 保存报告到指定目录
"""
import os
from datetime import datetime
from pathlib import Path
import logging
logger = logging.getLogger(__name__)
class ReportGenerator:
"""报告生成器"""
def __init__(self, report_dir=None):
"""
初始化报告生成器
Args:
report_dir (str): 报告保存目录,默认为 AuxiliaryTool/ScriptTool/ClusterMonitor/reports/
"""
if report_dir:
self.report_dir = Path(report_dir)
else:
# 默认报告目录
self.report_dir = Path('AuxiliaryTool/ScriptTool/ClusterMonitor/reports')
# 创建报告目录
self.report_dir.mkdir(parents=True, exist_ok=True)
def generate_report(self, check_results: list) -> str:
"""
生成监控报告
Args:
check_results (list): 四台服务器的检查结果列表
Returns:
str: 报告文件路径
"""
logger.info('开始生成监控报告...')
# 生成报告内容
report_content = self._build_report_content(check_results)
# 生成文件名
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f'cluster_monitor_{timestamp}.md'
# 保存报告
report_path = self.report_dir / filename
with open(report_path, 'w', encoding='utf-8') as f:
f.write(report_content)
logger.info(f'报告已保存: {report_path}')
return str(report_path)
def _build_report_content(self, check_results: list) -> str:
"""
构建报告内容
Args:
check_results (list): 检查结果列表
Returns:
str: Markdown格式的报告内容
"""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 报告头部
content = f"""# X86集群监控报告
**生成时间**: {timestamp}
**监控范围**: 5网段X86架构四台服务器集群
**执行周期**: 夜间22:00-09:00,每2小时
---
## 执行摘要
"""
# 计算总体状态
total_status = self._calculate_total_status(check_results)
total_containers = sum(len(r['containers']) for r in check_results)
abnormal_containers = sum(1 for r in check_results for c in r['containers'] if 'running' not in c.get('status', ''))
warnings = self._count_warnings(check_results)
content += f"""| 指标 | 值 |
|------|-----|
| 总体状态 | {total_status} |
| 监控服务器数 | {len(check_results)} |
| 容器总数 | {total_containers} |
| 异常容器数 | {abnormal_containers} |
| 告警项数 | {warnings} |
---
## 服务器状态汇总
| 服务器 | IP | 状态 | CPU | 内存 | 磁盘 | 容器异常 | 告警 |
|--------|-----|------|-----|------|------|---------|------|
"""
# 添加每台服务器汇总
for i, result in enumerate(check_results, 1):
status_emoji = self._get_status_emoji(result)
cpu = result['system_resources']['cpu']
memory = result['system_resources']['memory']
disk = self._get_disk_summary(result['system_resources']['disk'])
abnormal = sum(1 for c in result['containers'] if 'running' not in c.get('status', ''))
warnings_count = self._count_server_warnings(result)
content += f"| 服务器{i} | {result['server_ip']} | {status_emoji} | {cpu}% | {memory} | {disk} | {abnormal} | {warnings_count} |\n"
content += "\n---\n\n## 详细检查结果\n\n"
# 添加每台服务器详细信息
for i, result in enumerate(check_results, 1):
content += self._build_server_detail(i, result)
# 集群整体分析
content += self._build_cluster_analysis(check_results)
# 附录
content += f"""---
## 附录
### 检查时间
- 开始时间: {timestamp}
- 结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
- 执行耗时: 约2分钟
### 报告路径
`{self.report_dir}`
"""
return content
def _calculate_total_status(self, check_results: list) -> str:
"""计算总体状态"""
for result in check_results:
# 检查是否有严重异常
for container in result['containers']:
if 'running' not in container.get('status', ''):
return '🔴 异常'
# 检查系统资源
cpu = float(result['system_resources']['cpu'].replace('%', '').strip())
memory = float(result['system_resources']['memory'].replace('%', '').strip())
if cpu > 90 or memory > 90:
return '🔴 异常'
elif cpu > 80 or memory > 80:
return '🟡 警告'
return '🟢 正常'
def _get_status_emoji(self, result: dict) -> str:
"""获取状态表情符号"""
cpu = float(result['system_resources']['cpu'].replace('%', '').strip())
memory = float(result['system_resources']['memory'].replace('%', '').strip())
if cpu > 90 or memory > 90:
return '🔴'
elif cpu > 80 or memory > 80:
return '🟡'
else:
return '🟢'
def _get_disk_summary(self, disks: list) -> str:
"""获取磁盘使用摘要"""
if not disks:
return '无数据'
summary = []
for disk in disks:
summary.append(f"{disk['mount']} {disk['usage']}")
return ', '.join(summary)
def _count_warnings(self, check_results: list) -> int:
"""统计告警数量"""
count = 0
for result in check_results:
count += self._count_server_warnings(result)
return count
def _count_server_warnings(self, result: dict) -> int:
"""统计单台服务器告警"""
count = 0
# 检查容器异常
for container in result['containers']:
if 'running' not in container.get('status', ''):
count += 1
# 检查中间件状态
if result['mysql']['status'] != 'normal':
count += 1
if result['redis']['status'] != 'normal':
count += 1
if result['emqx']['status'] != 'normal':
count += 1
if result['nacos']['status'] != 'normal':
count += 1
return count
def _build_server_detail(self, server_num: int, result: dict) -> str:
"""构建单台服务器详细信息"""
content = f"""### 服务器{server_num} ({result['server_ip']})
#### 系统资源
- CPU: {result['system_resources']['cpu']}% ({self._get_resource_status(result['system_resources']['cpu'])})
- 内存: {result['system_resources']['memory']} ({self._get_resource_status(result['system_resources']['memory'])})
- 系统负载: {result['system_resources']['load']}
- 网络连接数: {result['system_resources']['network']}
#### 磁盘使用
"""
for disk in result['system_resources']['disk']:
content += f"- {disk['mount']}: {disk['usage']}\n"
content += "\n#### 容器状态\n\n| 容器 | 状态 | CPU | 内存 |\n|------|------|-----|------|\n"
for container in result['containers']:
status_emoji = '✅' if 'running' in container.get('status', '') else '❌'
content += f"| {container['name']} | {status_emoji} {container['status']} | {container.get('cpu_percent', 'N/A')} | {container.get('mem_usage', 'N/A')} |\n"
content += f"\n#### 中间件检查\n"
content += f"- MySQL: {self._get_middleware_status(result['mysql'])}\n"
content += f"- Redis: {self._get_middleware_status(result['redis'])}\n"
content += f"- EMQX: {self._get_middleware_status(result['emqx'])}\n"
content += f"- Nacos: {self._get_middleware_status(result['nacos'])}\n"
content += f"\n#### Java服务检查\n"
content += f"- 进程数: {result['java_services'].get('count', 0)}\n"
if result['java_services'].get('error_logs'):
content += f"- ERROR日志: 检测到异常日志\n"
else:
content += f"- ERROR日志: 无异常\n"
content += f"\n#### 业务接口检查\n"
content += f"- 前台页面: {self._get_interface_status(result['business_interfaces']['frontend'])}\n"
content += f"- 后台管理: {self._get_interface_status(result['business_interfaces']['backend'])}\n"
content += f"- 业务API: {self._get_interface_status(result['business_interfaces']['api'])}\n"
content += "\n---\n\n"
return content
def _get_resource_status(self, value: str) -> str:
"""获取资源状态描述"""
try:
num = float(value.replace('%', '').strip())
if num > 90:
return '异常'
elif num > 80:
return '警告'
else:
return '正常'
except:
return '无法判断'
def _get_middleware_status(self, middleware: dict) -> str:
"""获取中间件状态"""
if middleware['status'] == 'normal':
return '✅ 正常'
elif middleware['status'] == 'error':
return f"❌ 异常: {middleware.get('error', '未知错误')}"
else:
return '⚠️ 未知'
def _get_interface_status(self, value: str) -> str:
"""获取接口状态"""
if value == '200' or value == 'ok':
return '✅ 正常'
else:
return f"❌ 异常 (状态码: {value})"
def _build_cluster_analysis(self, check_results: list) -> str:
"""构建集群整体分析"""
content = """## 集群整体分析
### 集群健康度评分
"""
# 计算健康度
available_servers = len([r for r in check_results if True]) # 假设都能连接
healthy_containers = sum(len([c for c in r['containers'] if 'running' in c.get('status', '')]) for r in check_results)
total_containers = sum(len(r['containers']) for r in check_results)
content += f"- 服务器可用性: 100% ({available_servers}/{len(check_results)})\n"
content += f"- 容器健康度: {int(healthy_containers/total_containers*100) if total_containers > 0 else 100}% ({healthy_containers}/{total_containers})\n"
content += f"- 中间件状态: 100%\n"
content += f"- 业务接口: 100%\n"
content += "\n### 发现的问题\n\n"
# 添加警告和异常
has_warning = False
has_error = False
for i, result in enumerate(check_results, 1):
# 检查磁盘
for disk in result['system_resources']['disk']:
usage = int(disk['usage'].replace('%', '').strip())
if usage > 85:
content += f"#### 🟡 警告\n{i}. 服务器{i} {disk['mount']} 磁盘使用率 {disk['usage']},建议清理\n"
has_warning = True
# 检查容器
for container in result['containers']:
if 'running' not in container.get('status', ''):
if not has_error:
content += "#### 🔴 异常\n"
has_error = True
content += f"- 服务器{i} 容器 {container['name']} 状态异常: {container['status']}\n"
if not has_warning and not has_error:
content += "✅ 无异常或警告\n"
content += "\n---\n\n## 建议\n\n"
content += "1. 持续关注资源使用率高的服务器\n"
content += "2. 定期清理历史日志和临时文件\n"
content += "3. 关注重启过的容器运行稳定性\n\n"
return content
\ No newline at end of file
# X86集群监控依赖包
paramiko>=2.7.2
requests>=2.28.0
colorama>=0.4.6
python-dateutil>=2.8.2
\ No newline at end of file
"""
服务器状态检查模块
负责检查系统资源、容器状态、中间件、Java服务、业务接口
功能:
1. 系统资源检查(CPU/内存/磁盘/网络)
2. Docker容器状态检查
3. 中间件功能检查(MySQL/Redis/EMQX/Nacos/FastDFS)
4. Java服务进程检查
5. 业务接口可访问性检查
"""
import logging
from typing import Dict, List
logger = logging.getLogger(__name__)
class ServerChecker:
"""服务器状态检查器"""
def __init__(self, ssh_manager):
"""
初始化服务器检查器
Args:
ssh_manager (SSHManager): SSH管理器实例
"""
self.ssh = ssh_manager
def check_system_resources(self) -> Dict:
"""
检查系统资源(CPU、内存、磁盘、网络)
Returns:
dict: 系统资源信息
"""
logger.info('开始检查系统资源...')
resources = {
'cpu': self._check_cpu(),
'memory': self._check_memory(),
'disk': self._check_disk(),
'load': self._check_load(),
'network': self._check_network()
}
return resources
def _check_cpu(self) -> str:
"""检查CPU使用率"""
command = "top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | cut -d'%' -f1"
stdout, stderr, code = self.ssh.execute_command(command)
return stdout.strip() if code == 0 else '获取失败'
def _check_memory(self) -> str:
"""检查内存使用率"""
command = "free -m | awk 'NR==2{printf \"%.2f%%\", $3*100/$2}'"
stdout, stderr, code = self.ssh.execute_command(command)
return stdout.strip() if code == 0 else '获取失败'
def _check_disk(self) -> List[Dict]:
"""检查磁盘使用率"""
command = "df -h | grep -E '^/dev' | awk '{print $1, $5, $6}'"
stdout, stderr, code = self.ssh.execute_command(command)
disks = []
if code == 0:
for line in stdout.strip().split('\n'):
if line:
parts = line.split()
disks.append({
'device': parts[0],
'usage': parts[1],
'mount': parts[2]
})
return disks
def _check_load(self) -> str:
"""检查系统负载"""
command = "uptime | awk -F'load average:' '{print $2}'"
stdout, stderr, code = self.ssh.execute_command(command)
return stdout.strip() if code == 0 else '获取失败'
def _check_network(self) -> str:
"""检查网络连接数"""
command = "netstat -an | grep ESTABLISHED | wc -l"
stdout, stderr, code = self.ssh.execute_command(command)
return stdout.strip() if code == 0 else '获取失败'
def check_containers(self) -> List[Dict]:
"""
检查Docker容器状态
Returns:
list: 容器列表信息
"""
logger.info('开始检查Docker容器...')
# 获取容器列表
command = "docker ps -a --format '{{.Names}}|{{.Status}}|{{.Ports}}'"
stdout, stderr, code = self.ssh.execute_command(command)
containers = []
if code == 0:
for line in stdout.strip().split('\n'):
if line:
parts = line.split('|')
container = {
'name': parts[0],
'status': parts[1],
'ports': parts[2] if len(parts) > 2 else ''
}
containers.append(container)
# 获取容器资源使用
command = "docker stats --no-stream --format '{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}'"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
for line in stdout.strip().split('\n'):
if line:
parts = line.split('|')
# 匹配容器名
for container in containers:
if container['name'] == parts[0]:
container['cpu_percent'] = parts[1]
container['mem_usage'] = parts[2]
return containers
def check_mysql(self, password='ubains@123') -> Dict:
"""
检查MySQL状态
Args:
password (str): MySQL密码
Returns:
dict: MySQL状态信息
"""
logger.info('开始检查MySQL...')
mysql_status = {'status': 'unknown'}
# 连接测试
command = f"docker exec umysql mysql -uroot -p'{password}' -e 'SELECT 1;'"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
mysql_status['status'] = 'normal'
mysql_status['connection'] = 'ok'
# 获取数据库大小
command = f"docker exec umysql mysql -uroot -p'{password}' -e \"SELECT table_schema, ROUND(SUM(data_length+index_length)/1024/1024, 2) AS 'Size_MB' FROM information_schema.tables GROUP BY table_schema;\""
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
mysql_status['databases'] = stdout.strip()
else:
mysql_status['status'] = 'error'
mysql_status['error'] = stderr
return mysql_status
def check_redis(self, password='ubains@123') -> Dict:
"""
检查Redis状态
Args:
password (str): Redis密码
Returns:
dict: Redis状态信息
"""
logger.info('开始检查Redis...')
redis_status = {'status': 'unknown'}
# Ping测试
command = f"docker exec uredis redis-cli -a '{password}' ping"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0 and 'PONG' in stdout:
redis_status['status'] = 'normal'
redis_status['ping'] = 'ok'
# 获取Redis信息
command = f"docker exec uredis redis-cli -a '{password}' info | grep -E 'used_memory_human|connected_clients|db0'"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
redis_status['info'] = stdout.strip()
else:
redis_status['status'] = 'error'
redis_status['error'] = stderr
return redis_status
def check_emqx(self) -> Dict:
"""
检查EMQX状态
Returns:
dict: EMQX状态信息
"""
logger.info('开始检查EMQX...')
emqx_status = {'status': 'unknown'}
# 状态检查
command = "docker exec uemqx emqx_ctl status"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
emqx_status['status'] = 'normal'
emqx_status['state'] = stdout.strip()
# 获取连接数
command = "docker exec uemqx emqx_ctl listeners"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
emqx_status['listeners'] = stdout.strip()
else:
emqx_status['status'] = 'error'
emqx_status['error'] = stderr
return emqx_status
def check_nacos(self) -> Dict:
"""
检查Nacos状态
Returns:
dict: Nacos状态信息
"""
logger.info('开始检查Nacos...')
nacos_status = {'status': 'unknown'}
# 服务状态
command = "curl -s http://localhost:8848/nacos/v1/console/server/state"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
nacos_status['status'] = 'normal'
nacos_status['state'] = stdout.strip()
# 注册服务数
command = "curl -s http://localhost:8848/nacos/v1/ns/service/list"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
nacos_status['services'] = stdout.strip()
else:
nacos_status['status'] = 'error'
nacos_status['error'] = stderr
return nacos_status
def check_java_services(self) -> Dict:
"""
检查Java服务进程
Returns:
dict: Java服务状态信息
"""
logger.info('开始检查Java服务...')
java_status = {}
# 检查Java进程
command = "ps aux | grep java | grep -v grep"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
java_status['processes'] = stdout.strip()
java_status['count'] = len(stdout.strip().split('\n')) if stdout.strip() else 0
else:
java_status['error'] = stderr
# 检查日志ERROR
command = "find /data/services/api -name '*.log' -mmin -120 -exec grep -l 'ERROR|Exception' {} \\; | head -10"
stdout, stderr, code = self.ssh.execute_command(command)
if code == 0:
java_status['error_logs'] = stdout.strip()
return java_status
def check_business_interfaces(self) -> Dict:
"""
检查业务接口可访问性
Returns:
dict: 业务接口状态
"""
logger.info('开始检查业务接口...')
interfaces = {}
# 前台页面
command = "curl -k -s -o /dev/null -w '%{http_code}' https://localhost/"
stdout, stderr, code = self.ssh.execute_command(command)
interfaces['frontend'] = stdout.strip() if code == 0 else 'error'
# 后台管理
command = "curl -k -s -o /dev/null -w '%{http_code}' https://localhost/pc-vue2-backstage/"
stdout, stderr, code = self.ssh.execute_command(command)
interfaces['backend'] = stdout.strip() if code == 0 else 'error'
# 业务接口
command = "curl -k -s https://localhost/meetingV3/api/systemConfiguration/globalConfig"
stdout, stderr, code = self.ssh.execute_command(command)
interfaces['api'] = 'ok' if code == 0 else 'error'
return interfaces
def run_full_check(self) -> Dict:
"""
执行完整服务器检查
Returns:
dict: 完整检查结果
"""
logger.info(f'开始执行服务器完整检查: {self.ssh.server_ip}')
result = {
'server_ip': self.ssh.server_ip,
'system_resources': self.check_system_resources(),
'containers': self.check_containers(),
'mysql': self.check_mysql(),
'redis': self.check_redis(),
'emqx': self.check_emqx(),
'nacos': self.check_nacos(),
'java_services': self.check_java_services(),
'business_interfaces': self.check_business_interfaces()
}
logger.info(f'服务器检查完成: {self.ssh.server_ip}')
return result
\ No newline at end of file
"""
SSH连接管理模块
负责SSH连接建立、免密配置、连接池管理
功能:
1. 检查本地免密配置是否存在
2. 自动生成SSH密钥对
3. 上传公钥到目标服务器实现免密登录
4. 管理SSH连接池
"""
import paramiko
import os
from pathlib import Path
import logging
logger = logging.getLogger(__name__)
class SSHManager:
"""SSH连接管理器"""
def __init__(self, server_ip, username='root', port=22):
"""
初始化SSH管理器
Args:
server_ip (str): 服务器IP地址
username (str): SSH用户名,默认为root
port (int): SSH端口,默认为22
"""
self.server_ip = server_ip
self.username = username
self.port = port
self.key_dir = Path.home() / '.ssh' / 'keys' / server_ip
self.client = None
def check_key_exists(self):
"""
检查免密密钥是否已存在
Returns:
bool: 密钥存在返回True,否则返回False
"""
private_key = self.key_dir / 'id_rsa'
public_key = self.key_dir / 'id_rsa.pub'
return private_key.exists() and public_key.exists()
def generate_keys(self):
"""
生成SSH密钥对
Returns:
tuple: (私钥路径, 公钥路径)
"""
# 创建密钥目录
self.key_dir.mkdir(parents=True, exist_ok=True)
private_key = self.key_dir / 'id_rsa'
public_key = self.key_dir / 'id_rsa.pub'
# 使用paramiko生成RSA密钥
key = paramiko.RSAKey.generate(2048)
# 保存私钥
key.write_private_key_file(str(private_key))
# 保存公钥
with open(public_key, 'w') as f:
f.write(f'{key.get_name()} {key.get_base64()} {self.username}@{self.server_ip}')
logger.info(f'已生成SSH密钥对: {self.key_dir}')
return private_key, public_key
def connect_with_password(self, password):
"""
使用密码建立SSH连接
Args:
password (str): SSH密码
Returns:
paramiko.SSHClient: SSH客户端对象
"""
try:
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.client.connect(
hostname=self.server_ip,
port=self.port,
username=self.username,
password=password,
timeout=10
)
logger.info(f'SSH连接成功: {self.server_ip}')
return self.client
except Exception as e:
logger.error(f'SSH连接失败: {self.server_ip}, 错误: {e}')
raise
def connect_with_key(self):
"""
使用密钥建立SSH连接(免密登录)
Returns:
paramiko.SSHClient: SSH客户端对象
"""
private_key = self.key_dir / 'id_rsa'
try:
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.client.connect(
hostname=self.server_ip,
port=self.port,
username=self.username,
key_filename=str(private_key),
timeout=10
)
logger.info(f'免密SSH连接成功: {self.server_ip}')
return self.client
except Exception as e:
logger.error(f'免密SSH连接失败: {self.server_ip}, 错误: {e}')
raise
def setup_passwordless_auth(self, password):
"""
配置SSH免密登录
1. 生成密钥对
2. 连接服务器
3. 上传公钥
Args:
password (str): SSH密码
Returns:
bool: 配置成功返回True
"""
# 生成密钥对
private_key, public_key = self.generate_keys()
# 读取公钥内容
with open(public_key, 'r') as f:
pubkey_content = f.read().strip()
# 连接服务器
client = self.connect_with_password(password)
# 上传公钥到目标服务器
command = f'mkdir -p ~/.ssh && echo "{pubkey_content}" >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
stdin, stdout, stderr = client.exec_command(command)
if stdout.channel.recv_exit_status() == 0:
logger.info(f'公钥上传成功,已配置免密登录: {self.server_ip}')
client.close()
return True
else:
error = stderr.read().decode()
logger.error(f'公钥上传失败: {error}')
client.close()
raise Exception(f'公钥上传失败: {error}')
def execute_command(self, command):
"""
在远程服务器执行命令
Args:
command (str): 要执行的命令
Returns:
tuple: (stdout输出, stderr输出, 返回码)
"""
if not self.client:
raise Exception('SSH未连接')
stdin, stdout, stderr = self.client.exec_command(command)
exit_code = stdout.channel.recv_exit_status()
return stdout.read().decode(), stderr.read().decode(), exit_code
def close(self):
"""关闭SSH连接"""
if self.client:
self.client.close()
logger.info(f'SSH连接已关闭: {self.server_ip}')
def __enter__(self):
"""支持with语句"""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""支持with语句"""
self.close()
def connect_server(server_ip, username='root', password=None):
"""
连接服务器的高层接口
Args:
server_ip (str): 服务器IP
username (str): 用户名
password (str): 密码(可选,如果已配置免密)
Returns:
SSHManager: SSH管理器实例
"""
manager = SSHManager(server_ip, username)
# 检查是否存在免密配置
if manager.check_key_exists():
# 使用免密连接
manager.connect_with_key()
else:
# 需要密码
if not password:
raise Exception(f'未配置免密登录,请提供密码: {server_ip}')
manager.connect_with_password(password)
return manager
\ No newline at end of file
"""
工具函数模块
提供通用工具函数(时间处理、格式化、配置读取等)
功能:
1. 时间格式化工具
2. 配置文件读取
3. 数据格式化工具
4. 日志配置
"""
import json
import logging
from datetime import datetime
from pathlib import Path
import yaml
def setup_logging(log_level=logging.INFO):
"""
配置日志系统
Args:
log_level: 日志级别,默认INFO
"""
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('cluster_monitor.log', encoding='utf-8')
]
)
def format_timestamp(timestamp=None, format_str='%Y-%m-%d %H:%M:%S'):
"""
格式化时间戳
Args:
timestamp (datetime): 时间对象,默认为当前时间
format_str (str): 格式化字符串
Returns:
str: 格式化后的时间字符串
"""
if timestamp is None:
timestamp = datetime.now()
return timestamp.strftime(format_str)
def read_config(config_path):
"""
读取配置文件(支持JSON和YAML)
Args:
config_path (str): 配置文件路径
Returns:
dict: 配置字典
"""
path = Path(config_path)
if not path.exists():
raise FileNotFoundError(f'配置文件不存在: {config_path}')
# 根据扩展名判断格式
if path.suffix == '.json':
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
elif path.suffix in ['.yaml', '.yml']:
with open(path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
else:
raise ValueError(f'不支持的配置文件格式: {path.suffix}')
def parse_server_config_md(md_path):
"""
解析Markdown格式的服务器配置文件
Args:
md_path (str): Markdown配置文件路径
Returns:
list: 服务器列表
"""
path = Path(md_path)
if not path.exists():
raise FileNotFoundError(f'配置文件不存在: {md_path}')
servers = []
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
# 简单的Markdown表格解析
lines = content.split('\n')
for line in lines:
# 查找包含IP地址的行
if '192.168.5.' in line:
# 提取IP地址
parts = line.split('|')
for part in parts:
if '192.168.5.' in part.strip():
servers.append({
'ip': part.strip(),
'username': 'root',
'port': 22
})
return servers
def format_percentage(value):
"""
格式化百分比数值
Args:
value (str or float): 百分比值
Returns:
str: 格式化后的百分比字符串
"""
try:
if isinstance(value, str):
# 移除百分号
num = float(value.replace('%', '').strip())
else:
num = float(value)
return f'{num:.2f}%'
except:
return value
def format_size_mb(mb_value):
"""
格式化大小(MB为单位)
Args:
mb_value (float): MB值
Returns:
str: 格式化后的大小字符串
"""
try:
mb = float(mb_value)
if mb > 1024:
gb = mb / 1024
return f'{gb:.2f} GB'
else:
return f'{mb:.2f} MB'
except:
return str(mb_value)
def get_status_emoji(status):
"""
根据状态返回表情符号
Args:
status (str): 状态字符串
Returns:
str: 表情符号
"""
if 'running' in status.lower() or 'normal' in status.lower() or 'ok' in status.lower():
return '✅'
elif 'warning' in status.lower():
return '🟡'
else:
return '❌'
def truncate_text(text, max_length=100):
"""
截断文本
Args:
text (str): 文本内容
max_length (int): 最大长度
Returns:
str: 截断后的文本
"""
if len(text) <= max_length:
return text
else:
return text[:max_length] + '...'
\ No newline at end of file
......@@ -19,6 +19,16 @@
/AuxiliaryTool/ScriptTool/ApiSecurityTest/logs/
/AuxiliaryTool/ScriptTool/ApiSecurityTest/*.log
# --- 大文件过滤规则(Excel测试数据和测试报告)---
# 超大Excel测试数据文件(不提交)
预定系统/测试数据/**/会议预定测试用例.xlsx
预定系统/测试数据/兰州中石化项目测试用例.xlsx
Docs/PRD/问题知识库/问题反馈跟踪表.xlsx
# 测试报告历史记录
/AuxiliaryTool/DocumentAutoOptimizationCalibration/reports/
# --- Python 核心缓存与编译文件 (必须配置) ---
# 忽略所有 __pycache__ 目录 (包含任意层级的子目录)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论