feat: 新增设备巡检模块实现完整的巡检告警功能
新增设备巡检模块核心功能,包括: - 巡检数据存储与调度系统 - 华为/H3C交换机及Linux服务器采集器 - 设备扩展配置与告警规则管理 - 主机详情页巡检配置入口 - 产品名称自定义设置功能 - 网络设备SSH exec通道兼容处理
This commit is contained in:
174
electron/bridges/inspection/adapters/huaweiH3CSwitchAdapter.cjs
Normal file
174
electron/bridges/inspection/adapters/huaweiH3CSwitchAdapter.cjs
Normal file
@@ -0,0 +1,174 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* 华为 / H3C 交换机采集器
|
||||
*
|
||||
* 采集指标:
|
||||
* - cpu_usage CPU 使用率(%)
|
||||
* - memory_usage 内存使用率(%)
|
||||
* - port_status 端口状态 { up_count, down_count }
|
||||
* - power_status 电源状态 { normal_count, abnormal_count }
|
||||
* - fan_status 风扇状态
|
||||
*
|
||||
* 实现说明:
|
||||
* 使用批量命令执行,一次连接执行所有命令,避免 VTY 超时
|
||||
*/
|
||||
|
||||
const BaseAdapter = require("./baseAdapter.cjs");
|
||||
|
||||
class HuaweiH3CSwitchAdapter extends BaseAdapter {
|
||||
constructor(connectionInfo, options = {}) {
|
||||
super(connectionInfo, options);
|
||||
this.adapterName = "huawei_h3c_switch";
|
||||
if (options.execCommands) {
|
||||
this.execCommands = options.execCommands;
|
||||
}
|
||||
}
|
||||
|
||||
async execCommands(commands, options = {}) {
|
||||
const { execSshCommands } = require("../inspectionExec.cjs");
|
||||
return await execSshCommands(this.connectionInfo, commands, options);
|
||||
}
|
||||
|
||||
getSupportedMetrics() {
|
||||
return [
|
||||
"cpu_usage",
|
||||
"memory_usage",
|
||||
"port_status",
|
||||
"power_status",
|
||||
"fan_status",
|
||||
];
|
||||
}
|
||||
|
||||
async collectMetrics() {
|
||||
const metrics = {};
|
||||
const DEBUG = process.env.INSPECTION_DEBUG === "1";
|
||||
|
||||
if (DEBUG) console.log("[HuaweiH3C] 开始采集交换机指标");
|
||||
|
||||
const commands = [
|
||||
"display cpu-usage",
|
||||
"display memory",
|
||||
"display interface brief",
|
||||
"display power",
|
||||
"display fan",
|
||||
];
|
||||
|
||||
try {
|
||||
const { results } = await this.execCommands(commands, { timeoutMs: 180000 });
|
||||
|
||||
if (DEBUG) {
|
||||
console.log("[HuaweiH3C] 原始输出:");
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
// 简化日志,不输出完整原始数据
|
||||
const out = results[i]?.output || "";
|
||||
console.log(`[HuaweiH3C] 命令${i + 1}: ${commands[i]} → ${out.length} 字符`);
|
||||
}
|
||||
}
|
||||
|
||||
if (results && results.length >= 5) {
|
||||
metrics.cpu_usage = this.parseCpuUsage(results[0]?.output || "");
|
||||
metrics.memory_usage = this.parseMemoryUsage(results[1]?.output || "");
|
||||
metrics.port_status = this.parsePorts(results[2]?.output || "");
|
||||
metrics.power_status = this.parsePower(results[3]?.output || "");
|
||||
metrics.fan_status = this.parseFan(results[4]?.output || "");
|
||||
}
|
||||
|
||||
if (DEBUG) console.log("[HuaweiH3C] Final:", JSON.stringify(metrics));
|
||||
} catch (e) {
|
||||
if (DEBUG) console.log("[HuaweiH3C] Error:", e.message);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
parseCpuUsage(output) {
|
||||
// H3C Comware 5/7: "7% in last 5 seconds"
|
||||
let m = output.match(/(\d+)\s*%\s*in\s+last\s+5\s+seconds/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
// 1 minute
|
||||
m = output.match(/(\d+)\s*%\s*in\s+last\s+1\s+minute/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
// 华为格式
|
||||
m = output.match(/CPU\s+Usage\s*:\s*(\d+)%/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
// 5 Seconds
|
||||
m = output.match(/5\s*Seconds\s*[::]\s*(\d+)\s*%/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
return null;
|
||||
}
|
||||
|
||||
parseMemoryUsage(output) {
|
||||
// H3C: "Used Rate: 45%"
|
||||
let m = output.match(/Used\s*Rate\s*[::]?\s*(\d+(?:\.\d+)?)\s*%/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
// 华为/其他: "Memory Using Percentage Is: 21%"
|
||||
m = output.match(/Memory\s+Using\s+Percentage\s+Is\s*[::]?\s*(\d+(?:\.\d+)?)\s*%/i);
|
||||
if (m) return parseFloat(m[1]);
|
||||
// H3C bytes 格式: "System Total Memory(bytes): 65546160" / "Total Used Memory(bytes): 29528544"
|
||||
const totalM = output.match(/System\s+Total\s+Memory\s*\(\s*bytes\s*\)\s*[::]?\s*(\d+)/i);
|
||||
const usedM = output.match(/Total\s+Used\s+Memory\s*\(\s*bytes\s*\)\s*[::]?\s*(\d+)/i);
|
||||
if (totalM && usedM) {
|
||||
const total = parseFloat(totalM[1]);
|
||||
const used = parseFloat(usedM[1]);
|
||||
if (total > 0) return Math.round((used / total) * 100 * 10) / 10;
|
||||
}
|
||||
// MB 格式: "Memory Size: xxx MB"
|
||||
const totalMB = output.match(/Memory\s*(?:Size|Total)\s*[::]?\s*(\d+)\s*MB/i);
|
||||
const usedMB = output.match(/Memory\s*Used\s*[::]?\s*(\d+)\s*MB/i);
|
||||
if (totalMB && usedMB) {
|
||||
const total = parseFloat(totalMB[1]);
|
||||
const used = parseFloat(usedMB[1]);
|
||||
if (total > 0) return Math.round((used / total) * 100 * 10) / 10;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
parsePorts(output) {
|
||||
const lines = output.split("\n");
|
||||
let upCount = 0, downCount = 0;
|
||||
const ports = [];
|
||||
for (const line of lines) {
|
||||
// H3C 格式: "GE1/0/1 UP 100M(a)"
|
||||
// 匹配 GE开头 + UP/DOWN
|
||||
const geMatch = line.match(/^(GE\d+\/\d+\/\d+)\s+(UP|DOWN|up|down)/i);
|
||||
if (geMatch) {
|
||||
const phy = geMatch[2].toLowerCase();
|
||||
ports.push({ name: geMatch[1], phy });
|
||||
if (phy === "up") upCount++;
|
||||
else downCount++;
|
||||
continue;
|
||||
}
|
||||
// 匹配 GigabitEthernet 全称
|
||||
const gigMatch = line.match(/^(GigabitEthernet\d+[\d\/]+)\s+(UP|DOWN|up|down)/i);
|
||||
if (gigMatch) {
|
||||
const phy = gigMatch[2].toLowerCase();
|
||||
ports.push({ name: gigMatch[1], phy });
|
||||
if (phy === "up") upCount++;
|
||||
else downCount++;
|
||||
}
|
||||
}
|
||||
return { up_count: upCount, down_count: downCount, ports };
|
||||
}
|
||||
|
||||
parsePower(output) {
|
||||
let normal = 0, abnormal = 0;
|
||||
for (const line of output.split("\n")) {
|
||||
if (/Normal|Supply|OK/i.test(line)) normal++;
|
||||
else if (/Absent|Fault|Error/i.test(line)) abnormal++;
|
||||
}
|
||||
return { normal_count: normal, abnormal_count: abnormal };
|
||||
}
|
||||
|
||||
parseFan(output) {
|
||||
let normal = 0, abnormal = 0;
|
||||
for (const line of output.split("\n")) {
|
||||
if (/Normal|Auto|Running|OK/i.test(line)) normal++;
|
||||
else if (/Fault|Absent|Error/i.test(line)) abnormal++;
|
||||
}
|
||||
return { normal_count: normal, abnormal_count: abnormal };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HuaweiH3CSwitchAdapter;
|
||||
Reference in New Issue
Block a user