"use strict"; /** * 告警规则数据存储 * * 告警规则结构: * { * id: "rule_xxx", * name: "CPU 使用率过高", * metric: "cpu_usage", * operator: ">", * threshold: "80", * level: "critical", * duration: 1, // 连续超标次数(防抖动) * scope_type: "all", // all / tier / device / catalog * scope_value: null, // 范围值(tier_key 或 hostId 数组 或 catalog_id) * enabled: true, * created_at: timestamp, * updated_at: timestamp, * } */ const { STORAGE_KEY_ALERT_RULES } = require("./storageKeys.cjs"); const { OPERATORS, ALERT_LEVELS } = require("./types.cjs"); let _rules = null; function _ensureLoaded(readFn) { if (_rules === null) { const raw = readFn(STORAGE_KEY_ALERT_RULES); _rules = raw ? _safeParse(raw, []) : _getDefaultRules(); } } function _safeParse(text, def) { try { return JSON.parse(text); } catch (e) { return def; } } function _save(writeFn) { writeFn(STORAGE_KEY_ALERT_RULES, JSON.stringify(_rules)); } /** * 默认告警规则(预置) */ function _getDefaultRules() { return [ { id: "rule_cpu_critical", name: "CPU 使用率过高", metric: "cpu_usage", operator: OPERATORS.GT, threshold: "80", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_cpu_warning", name: "CPU 使用率异常", metric: "cpu_usage", operator: OPERATORS.GT, threshold: "70", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_memory_critical", name: "内存使用率过高", metric: "memory_usage", operator: OPERATORS.GT, threshold: "85", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_memory_warning", name: "内存使用率异常", metric: "memory_usage", operator: OPERATORS.GT, threshold: "75", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_offline", name: "设备离线", metric: "online", operator: OPERATORS.EQ, threshold: "false", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, // ---------- 磁盘使用率 ---------- { id: "rule_disk_critical", name: "磁盘使用率过高", metric: "disk_usage", operator: OPERATORS.GT, threshold: "90", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_disk_warning", name: "磁盘使用率异常", metric: "disk_usage", operator: OPERATORS.GT, threshold: "80", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "all", scope_value: null, enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, // ---------- 网络设备通用(交换机/防火墙) ---------- { id: "rule_netdev_temp_critical", name: "设备温度过高", metric: "temperature", operator: OPERATORS.GT, threshold: "60", level: ALERT_LEVELS.CRITICAL, duration: 2, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_netdev_power_critical", name: "电源模块故障", metric: "power_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_netdev_fan_warning", name: "风扇模块故障", metric: "fan_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.WARNING, duration: 1, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_netdev_portdown_warning", name: "端口 Down 数过多", metric: "port_down_count", operator: OPERATORS.GT, threshold: "2", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_netdev_cpu_warning", name: "交换机 CPU 使用率过高", metric: "cpu_usage", operator: OPERATORS.GT, threshold: "75", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_netdev_memory_warning", name: "交换机内存使用率过高", metric: "memory_usage", operator: OPERATORS.GT, threshold: "80", level: ALERT_LEVELS.WARNING, duration: 2, scope_type: "catalog", scope_value: "huawei_h3c_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, // ---------- Cisco 交换机 ---------- { id: "rule_cisco_temp_critical", name: "Cisco 交换机温度过高", metric: "temperature", operator: OPERATORS.GT, threshold: "60", level: ALERT_LEVELS.CRITICAL, duration: 2, scope_type: "catalog", scope_value: "cisco_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_cisco_power_critical", name: "Cisco 电源模块故障", metric: "power_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "catalog", scope_value: "cisco_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_cisco_fan_warning", name: "Cisco 风扇模块故障", metric: "fan_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.WARNING, duration: 1, scope_type: "catalog", scope_value: "cisco_switch", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, // ---------- 华为防火墙 ---------- { id: "rule_hwfw_temp_critical", name: "华为防火墙温度过高", metric: "temperature", operator: OPERATORS.GT, threshold: "60", level: ALERT_LEVELS.CRITICAL, duration: 2, scope_type: "catalog", scope_value: "huawei_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_hwfw_power_critical", name: "华为防火墙电源故障", metric: "power_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "catalog", scope_value: "huawei_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_hwfw_fan_warning", name: "华为防火墙风扇故障", metric: "fan_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.WARNING, duration: 1, scope_type: "catalog", scope_value: "huawei_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, // ---------- 飞塔防火墙 ---------- { id: "rule_fortinet_temp_critical", name: "飞塔防火墙温度过高", metric: "temperature", operator: OPERATORS.GT, threshold: "60", level: ALERT_LEVELS.CRITICAL, duration: 2, scope_type: "catalog", scope_value: "fortinet_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_fortinet_power_critical", name: "飞塔防火墙电源故障", metric: "power_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.CRITICAL, duration: 1, scope_type: "catalog", scope_value: "fortinet_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, { id: "rule_fortinet_fan_warning", name: "飞塔防火墙风扇故障", metric: "fan_failure_count", operator: OPERATORS.GT, threshold: "0", level: ALERT_LEVELS.WARNING, duration: 1, scope_type: "catalog", scope_value: "fortinet_firewall", enabled: true, preset: true, created_at: Date.now(), updated_at: Date.now(), }, ]; } // ---------- CRUD ---------- function listRules(readFn) { _ensureLoaded(readFn); return [..._rules]; } function getRule(readFn, ruleId) { _ensureLoaded(readFn); return _rules.find((r) => r.id === ruleId) || null; } function createRule(readFn, writeFn, data) { _ensureLoaded(readFn); const rule = { id: `rule_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, name: data.name, metric: data.metric, operator: data.operator, threshold: String(data.threshold), level: data.level, duration: data.duration || 1, scope_type: data.scope_type || "all", scope_value: data.scope_value || null, enabled: data.enabled !== false, preset: false, created_at: Date.now(), updated_at: Date.now(), }; _rules.push(rule); _save(writeFn); return { ...rule }; } function updateRule(readFn, writeFn, ruleId, updates) { _ensureLoaded(readFn); const idx = _rules.findIndex((r) => r.id === ruleId); if (idx === -1) return null; _rules[idx] = { ..._rules[idx], ...updates, updated_at: Date.now(), }; _save(writeFn); return { ..._rules[idx] }; } function deleteRule(readFn, writeFn, ruleId) { _ensureLoaded(readFn); const idx = _rules.findIndex((r) => r.id === ruleId); if (idx === -1) return false; _rules.splice(idx, 1); _save(writeFn); return true; } function toggleRule(readFn, writeFn, ruleId, enabled) { return updateRule(readFn, writeFn, ruleId, { enabled }); } // ---------- 告警判断 ---------- /** * 判断指标值是否触发规则 * @param {number|string} value - 指标值 * @param {string} operator - 比较符 * @param {string} threshold - 阈值 * @returns {boolean} */ function evaluateRule(value, operator, threshold) { if (value === null || value === undefined) return false; // disk_usage 是数组,检查任意挂载点是否超标 if (Array.isArray(value)) { const numThreshold = parseFloat(threshold); if (isNaN(numThreshold)) return false; // 数组中每个元素是 { filesystem, usage_percent, mount } return value.some((d) => { const usage = typeof d === "object" ? d.usage_percent : parseFloat(d); return !isNaN(usage) && usage > numThreshold; }); } // 数字比较 const numValue = parseFloat(value); const numThreshold = parseFloat(threshold); if (!isNaN(numValue) && !isNaN(numThreshold)) { switch (operator) { case OPERATORS.GT: return numValue > numThreshold; case OPERATORS.LT: return numValue < numThreshold; case OPERATORS.GTE: return numValue >= numThreshold; case OPERATORS.LTE: return numValue <= numThreshold; case OPERATORS.EQ: return numValue === numThreshold; case OPERATORS.NE: return numValue !== numThreshold; } } // 字符串比较 const strValue = String(value); const strThreshold = String(threshold); switch (operator) { case OPERATORS.EQ: return strValue === strThreshold; case OPERATORS.NE: return strValue !== strThreshold; default: return false; } } /** * 获取设备适用的所有规则 * @param {object} deviceExt - 设备扩展信息 * @returns {Array} 适用的规则列表 */ function getRulesForDevice(readFn, deviceExt) { _ensureLoaded(readFn); return _rules.filter((rule) => { if (!rule.enabled) return false; switch (rule.scope_type) { case "all": return true; case "tier": return deviceExt.tier && deviceExt.tier === rule.scope_value; case "device": return Array.isArray(rule.scope_value) && rule.scope_value.includes(deviceExt.host_id); case "catalog": return deviceExt.catalog_id === rule.scope_value; default: return false; } }); } // ---------- 重置(测试用) ---------- function _resetForTest() { _rules = null; } module.exports = { listRules, getRule, createRule, updateRule, deleteRule, toggleRule, evaluateRule, getRulesForDevice, _resetForTest, };