175 lines
5.7 KiB
JavaScript
175 lines
5.7 KiB
JavaScript
|
|
"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;
|