Files
SenMeshNet/electron/bridges/inspection/adapters/huaweiH3CSwitchAdapter.cjs
zhaolei 2f9f8da48e
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
Initial commit: NetMesh smart ops platform with 3D topology, inspection reports, RCA and AI ops assistant
2026-10-15 13:53:04 +08:00

175 lines
5.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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;