Initial commit: NetMesh smart ops platform with 3D topology, inspection reports, RCA and AI ops assistant
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
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
This commit is contained in:
543
electron/bridges/inspection/alertRuleStore.cjs
Normal file
543
electron/bridges/inspection/alertRuleStore.cjs
Normal file
@@ -0,0 +1,543 @@
|
||||
"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,
|
||||
};
|
||||
Reference in New Issue
Block a user