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
174 lines
4.1 KiB
JavaScript
174 lines
4.1 KiB
JavaScript
"use strict";
|
||
|
||
/**
|
||
* Prompt 解析器 - 从命令输出中提取指标
|
||
*
|
||
* 分层策略:
|
||
* 1. 正则匹配(快,简单场景够用)
|
||
* 2. 返回原始输出(解析失败时让用户看原始数据)
|
||
*/
|
||
|
||
/**
|
||
* 华为/H3C 交换机命令解析配置
|
||
*/
|
||
const HUAWEI_H3C_PATTERNS = {
|
||
cpu_usage: {
|
||
// CPU 使用率:多种可能的格式
|
||
patterns: [
|
||
/CPU\s+Usage\s*:\s*(\d+)%/i,
|
||
/CPU\s+utilization.*one\s+minute:\s*(\d+)%/i,
|
||
/CPU\s+Usage\s+:\s*(\d+)%/i,
|
||
],
|
||
default: null,
|
||
},
|
||
memory_usage: {
|
||
// 内存使用率:多种格式
|
||
patterns: [
|
||
/Memory\s+Using\s+Percentage\s+Is:\s*(\d+)%/i,
|
||
/Memory\s+utilization.*:\s*(\d+)%/i,
|
||
],
|
||
default: null,
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 解析单行指标
|
||
* @param {string} output - 命令原始输出
|
||
* @param {object} config - { patterns: RegExp[], default: any }
|
||
* @returns {any} 解析结果
|
||
*/
|
||
function parseMetric(output, config) {
|
||
if (!output || !config?.patterns) return config?.default ?? null;
|
||
|
||
for (const pattern of config.patterns) {
|
||
const match = output.match(pattern);
|
||
if (match) {
|
||
return parseFloat(match[1]);
|
||
}
|
||
}
|
||
return config.default ?? null;
|
||
}
|
||
|
||
/**
|
||
* 解析端口状态
|
||
* @param {string} output - 命令输出
|
||
* @returns {object} { up_count, down_count, ports }
|
||
*/
|
||
function parseInterfaceBrief(output) {
|
||
const lines = output.split("\n");
|
||
const ports = [];
|
||
let upCount = 0;
|
||
let downCount = 0;
|
||
|
||
for (const line of lines) {
|
||
// 匹配端口行:GigabitEthernet0/0/1 up up
|
||
const portMatch = line.match(/^(GigabitEthernet|FastEthernet|Ethernet)(\S+)\s+(\w+)\s+(\w+)/);
|
||
if (portMatch) {
|
||
const phy = portMatch[3].toLowerCase();
|
||
const proto = portMatch[4].toLowerCase();
|
||
ports.push({
|
||
name: portMatch[1] + portMatch[2],
|
||
phy,
|
||
protocol: proto,
|
||
is_up: phy === "up" && proto === "up",
|
||
});
|
||
if (phy === "up") upCount++;
|
||
else downCount++;
|
||
}
|
||
}
|
||
|
||
return { up_count: upCount, down_count: downCount, ports };
|
||
}
|
||
|
||
/**
|
||
* 解析电源状态
|
||
* @param {string} output - 命令输出
|
||
* @returns {object} { normal_count, abnormal_count }
|
||
*/
|
||
function parsePowerStatus(output) {
|
||
const lines = output.split("\n");
|
||
let normalCount = 0;
|
||
let abnormalCount = 0;
|
||
|
||
for (const line of lines) {
|
||
// 匹配电源状态行
|
||
if (/Normal|Supply|OK/i.test(line)) {
|
||
normalCount++;
|
||
} else if (/Absent|Fault|Error/i.test(line)) {
|
||
abnormalCount++;
|
||
}
|
||
}
|
||
|
||
return { normal_count: normalCount, abnormal_count: abnormalCount };
|
||
}
|
||
|
||
/**
|
||
* 清理交换机输出
|
||
* @param {string} output - 原始输出
|
||
* @returns {string} 清理后的输出
|
||
*/
|
||
function cleanOutput(output) {
|
||
return output
|
||
// 移除提示符
|
||
.replace(/<\w+[-A-Za-z0-9]*>/g, "")
|
||
// 移除分页标记
|
||
.replace(/---- More ----/g, "")
|
||
// 移除命令回显
|
||
.replace(/^[^\r\n]*\r?\n/, "")
|
||
// 移除空行
|
||
.replace(/\n{3,}/g, "\n\n")
|
||
.trim();
|
||
}
|
||
|
||
/**
|
||
* 使用 prompt 风格解析交换机输出
|
||
* @param {string} output - 原始命令输出
|
||
* @param {object} context - { catalog_id, device_info }
|
||
* @returns {object} 结构化指标
|
||
*/
|
||
function parseSwitchOutput(output, context = {}) {
|
||
const cleaned = cleanOutput(output);
|
||
const lines = cleaned.split("\n");
|
||
|
||
const metrics = {
|
||
cpu_usage: null,
|
||
memory_usage: null,
|
||
port_status: [],
|
||
power_status: [],
|
||
fan_status: [],
|
||
};
|
||
|
||
// 解析 CPU
|
||
metrics.cpu_usage = parseMetric(cleaned, HUAWEI_H3C_PATTERNS.cpu_usage);
|
||
|
||
// 解析内存
|
||
metrics.memory_usage = parseMetric(cleaned, HUAWEI_H3C_PATTERNS.memory_usage);
|
||
|
||
// 解析端口
|
||
metrics.port_status = parseInterfaceBrief(cleaned);
|
||
|
||
// 解析电源
|
||
metrics.power_status = parsePowerStatus(cleaned);
|
||
|
||
// 原始输出(便于调试)
|
||
metrics._raw_output = cleaned;
|
||
metrics._parse_errors = [];
|
||
|
||
if (metrics.cpu_usage === null) {
|
||
metrics._parse_errors.push("cpu_usage: 无法解析");
|
||
}
|
||
if (metrics.memory_usage === null) {
|
||
metrics._parse_errors.push("memory_usage: 无法解析");
|
||
}
|
||
|
||
return metrics;
|
||
}
|
||
|
||
module.exports = {
|
||
parseMetric,
|
||
parseInterfaceBrief,
|
||
parsePowerStatus,
|
||
cleanOutput,
|
||
parseSwitchOutput,
|
||
};
|