"use strict"; /** * 采集器基类 * * 所有设备采集器都继承此类,统一采集接口。 * 采集器负责: * 1. 建立 SSH 连接 * 2. 执行巡检命令 * 3. 解析输出为结构化指标数据 * 4. 关闭连接 * * 子类需实现: * - getSupportedMetrics() 返回支持的指标列表 * - collectMetrics() 执行所有采集命令并返回结构化指标 */ const { INSPECTION_STATUS } = require("../types.cjs"); class BaseAdapter { /** * @param {object} connectionInfo - SSH 连接信息 * @param {string} connectionInfo.host - 主机 IP * @param {number} connectionInfo.port - 端口 * @param {string} connectionInfo.username - 用户名 * @param {string} [connectionInfo.password] - 密码 * @param {string} [connectionInfo.privateKey] - 私钥 * @param {object} [options] * @param {number} [options.timeoutMs] - 单条命令超时(毫秒) */ constructor(connectionInfo, options = {}) { this.connectionInfo = connectionInfo; this.timeoutMs = options.timeoutMs || 15000; this.conn = null; } /** * 建立连接 * 子类可以覆盖此方法实现不同的连接方式(如 Telnet) * @returns {Promise} */ async connect() { // 基类不实现具体连接逻辑,由上层调度器负责 SSH 连接 // 或者由子类自行实现 return true; } /** * 执行单条命令 * 由子类或注入的 exec 函数实现 * @param {string} command * @returns {Promise} */ async execute(command) { throw new Error("execute() must be implemented by subclass or injected"); } /** * 关闭连接 */ async disconnect() { // 子类可覆盖 } /** * 获取支持的指标列表 * @returns {string[]} */ getSupportedMetrics() { throw new Error("getSupportedMetrics() must be implemented by subclass"); } /** * 执行完整巡检,返回结构化指标数据 * @returns {Promise} * { * online: boolean, * metrics: { ... }, * raw: { ... } // 原始输出(调试用) * } */ async collect() { const result = { online: false, metrics: {}, raw: {}, error: null, status: INSPECTION_STATUS.SUCCESS, }; try { const metrics = await this.collectMetrics(); result.online = true; result.metrics = metrics; } catch (err) { result.error = err.message; result.status = err.message.includes("timeout") ? INSPECTION_STATUS.TIMEOUT : INSPECTION_STATUS.FAILED; } return result; } /** * 子类实现:具体的采集逻辑 * @returns {Promise} 结构化指标数据 */ async collectMetrics() { throw new Error("collectMetrics() must be implemented by subclass"); } /** * 安全地从字符串中提取数字 * @param {string} text * @param {RegExp} regex * @returns {number|null} */ extractNumber(text, regex) { const match = (text || "").match(regex); if (!match) return null; const num = parseFloat(match[1]); return isNaN(num) ? null : num; } } module.exports = BaseAdapter;