"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;