160 lines
9.1 KiB
JavaScript
160 lines
9.1 KiB
JavaScript
|
|
"use strict";
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 预置命令 ID 目录(D4:白名单方式为主)
|
|||
|
|
*
|
|||
|
|
* 结构:
|
|||
|
|
* {
|
|||
|
|
* id: "switch_show_cpu",
|
|||
|
|
* name: "显示 CPU 使用率",
|
|||
|
|
* command: "display cpu-usage",
|
|||
|
|
* device_types: ["huawei_h3c_switch", "cisco_switch"],
|
|||
|
|
* risk_level: "low", // low | medium | high
|
|||
|
|
* execution: "auto" | "confirm" | "manual_only",
|
|||
|
|
* category: "diagnostic", // diagnostic | config | security | backup | recovery
|
|||
|
|
* description: "...",
|
|||
|
|
* }
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const { STORAGE_KEY_COMMAND_CATALOG } = require("./storageKeys.cjs");
|
|||
|
|
|
|||
|
|
let _catalog = null;
|
|||
|
|
|
|||
|
|
function _ensureLoaded(readFn) {
|
|||
|
|
if (_catalog === null) {
|
|||
|
|
const raw = readFn(STORAGE_KEY_COMMAND_CATALOG);
|
|||
|
|
_catalog = raw ? _safeParse(raw, []) : _getDefaultCatalog();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function _safeParse(text, def) {
|
|||
|
|
if (text === null || text === undefined) return def;
|
|||
|
|
if (typeof text === "object") return text;
|
|||
|
|
try { return JSON.parse(text); }
|
|||
|
|
catch { return def; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function _save(writeFn) {
|
|||
|
|
writeFn(STORAGE_KEY_COMMAND_CATALOG, JSON.stringify(_catalog));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function _getDefaultCatalog() {
|
|||
|
|
const now = Date.now();
|
|||
|
|
return [
|
|||
|
|
// ========== 通用诊断命令(auto)==========
|
|||
|
|
{ id: "linux_show_cpu", name: "查看 CPU 使用率", command: "top -bn1 | head -20", device_types: ["linux", "windows"], risk_level: "low", execution: "auto", category: "diagnostic", description: "一次性显示 CPU 负载", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "linux_show_mem", name: "查看内存使用率", command: "free -m", device_types: ["linux"], risk_level: "low", execution: "auto", category: "diagnostic", description: "查看内存使用情况", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "linux_show_disk", name: "查看磁盘使用率", command: "df -h", device_types: ["linux"], risk_level: "low", execution: "auto", category: "diagnostic", description: "查看各挂载点磁盘使用率", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "linux_show_processes", name: "查看进程列表", command: "ps aux --sort=-%cpu | head -20", device_types: ["linux"], risk_level: "low", execution: "auto", category: "diagnostic", description: "按 CPU 排序显示前 20 条进程", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
|
|||
|
|
// ========== 华为/H3C 交换机(auto)==========
|
|||
|
|
{ id: "switch_show_cpu", name: "显示 CPU 使用率", command: "display cpu-usage", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示 CPU 使用率明细", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_mem", name: "显示内存使用率", command: "display memory", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示内存使用情况", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_temp", name: "显示温度", command: "display temperature", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示各传感器温度", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_device", name: "显示设备信息", command: "display device", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示电源、风扇、板卡状态", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_version", name: "显示版本信息", command: "display version", device_types: ["huawei_h3c_switch", "cisco_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示软件版本、硬件型号", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_power", name: "显示电源状态", command: "display power", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示各电源模块状态", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_show_fan", name: "显示风扇状态", command: "display fan", device_types: ["huawei_h3c_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示各风扇模块状态", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
|
|||
|
|
// ========== 华为/H3C 交换机(confirm)==========
|
|||
|
|
{ id: "switch_collect_diagnostic", name: "采集诊断信息", command: "display diagnostic-information save", device_types: ["huawei_h3c_switch"], risk_level: "medium", execution: "confirm", category: "backup", description: "采集完整诊断信息并保存到设备存储", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_backup_config", name: "备份 running-config", command: "display current-configuration", device_types: ["huawei_h3c_switch", "cisco_switch"], risk_level: "medium", execution: "confirm", category: "backup", description: "导出当前运行配置", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "switch_adjust_cpu_threshold", name: "调整 CPU 告警阈值", command: "cpu-usage threshold 95 log", device_types: ["huawei_h3c_switch"], risk_level: "medium", execution: "confirm", category: "config", description: "调 CPU 告警阈值避免重复告警", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
|
|||
|
|
// ========== Cisco 交换机(auto)==========
|
|||
|
|
{ id: "cisco_show_cpu", name: "显示 CPU 使用率", command: "show processes cpu sorted", device_types: ["cisco_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "按 CPU 使用率排序显示进程", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "cisco_show_mem", name: "显示内存使用率", command: "show memory summary", device_types: ["cisco_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示内存使用摘要", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "cisco_show_interfaces", name: "显示接口状态", command: "show interfaces brief", device_types: ["cisco_switch"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示所有接口简要状态", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
|
|||
|
|
// ========== 防火墙(auto)==========
|
|||
|
|
{ id: "fw_show_version", name: "显示版本信息", command: "display version", device_types: ["huawei_firewall", "fortinet_firewall"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示防火墙软件版本", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
{ id: "fw_show_temp", name: "显示温度", command: "display temperature all", device_types: ["huawei_firewall"], risk_level: "low", execution: "auto", category: "diagnostic", description: "显示防火墙各传感器温度", builtin: true, created_at: now, updated_at: now },
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- CRUD ----------
|
|||
|
|
|
|||
|
|
function listCommands(readFn, filters = {}) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
let result = [..._catalog];
|
|||
|
|
if (filters.device_type) {
|
|||
|
|
result = result.filter((c) => c.device_types.includes(filters.device_type));
|
|||
|
|
}
|
|||
|
|
if (filters.execution) {
|
|||
|
|
result = result.filter((c) => c.execution === filters.execution);
|
|||
|
|
}
|
|||
|
|
if (filters.risk_level) {
|
|||
|
|
result = result.filter((c) => c.risk_level === filters.risk_level);
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getCommand(readFn, commandId) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
return _catalog.find((c) => c.id === commandId) || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function createCommand(readFn, writeFn, data) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
const cmd = {
|
|||
|
|
id: data.id || `cmd_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|||
|
|
name: data.name,
|
|||
|
|
command: data.command,
|
|||
|
|
device_types: data.device_types || [],
|
|||
|
|
risk_level: data.risk_level || "low",
|
|||
|
|
execution: data.execution || "auto",
|
|||
|
|
category: data.category || "diagnostic",
|
|||
|
|
description: data.description || "",
|
|||
|
|
builtin: false,
|
|||
|
|
created_at: Date.now(),
|
|||
|
|
updated_at: Date.now(),
|
|||
|
|
};
|
|||
|
|
_catalog.push(cmd);
|
|||
|
|
_save(writeFn);
|
|||
|
|
return { ...cmd };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateCommand(readFn, writeFn, commandId, updates) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
const idx = _catalog.findIndex((c) => c.id === commandId);
|
|||
|
|
if (idx === -1) return null;
|
|||
|
|
_catalog[idx] = { ..._catalog[idx], ...updates, updated_at: Date.now() };
|
|||
|
|
_save(writeFn);
|
|||
|
|
return { ..._catalog[idx] };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function deleteCommand(readFn, writeFn, commandId) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
const idx = _catalog.findIndex((c) => c.id === commandId);
|
|||
|
|
if (idx === -1) return false;
|
|||
|
|
if (_catalog[idx].builtin) return false; // 预置命令不可删
|
|||
|
|
_catalog.splice(idx, 1);
|
|||
|
|
_save(writeFn);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 批量校验 command_id 是否在目录中(D14:AI 生成预案交叉校验)
|
|||
|
|
*/
|
|||
|
|
function validateCommandIds(readFn, ids) {
|
|||
|
|
_ensureLoaded(readFn);
|
|||
|
|
if (!Array.isArray(ids)) return { valid: true, invalid: [] };
|
|||
|
|
const invalid = [];
|
|||
|
|
for (const id of ids) {
|
|||
|
|
if (!_catalog.find((c) => c.id === id)) invalid.push(id);
|
|||
|
|
}
|
|||
|
|
return { valid: invalid.length === 0, invalid };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function _resetForTest() { _catalog = null; }
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
listCommands,
|
|||
|
|
getCommand,
|
|||
|
|
createCommand,
|
|||
|
|
updateCommand,
|
|||
|
|
deleteCommand,
|
|||
|
|
validateCommandIds,
|
|||
|
|
_resetForTest,
|
|||
|
|
};
|