262 lines
7.5 KiB
Go
262 lines
7.5 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// GetAllModels 数据库迁移用模型列表
|
|
func GetAllModels() []interface{} {
|
|
return []interface{}{
|
|
&LogEvent{},
|
|
&AlertOutbox{},
|
|
&ResourceMapping{},
|
|
&ResourceEventDedup{},
|
|
&TrapDictionaryEntry{},
|
|
&SyslogRule{},
|
|
&TrapRule{},
|
|
&TrapShield{},
|
|
&AuditLog{},
|
|
&DangerousOperationApproval{},
|
|
}
|
|
}
|
|
|
|
// InitData 初始化默认规则数据(幂等)
|
|
func InitData(db *gorm.DB) error {
|
|
if db == nil {
|
|
return nil
|
|
}
|
|
if err := backfillDispatchOutboxIDs(db); err != nil {
|
|
return err
|
|
}
|
|
if err := seedDefaultSyslogRules(db); err != nil {
|
|
return err
|
|
}
|
|
if err := seedDefaultTrapRules(db); err != nil {
|
|
return err
|
|
}
|
|
if err := seedDefaultTrapDictionary(db); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func backfillDispatchOutboxIDs(db *gorm.DB) error {
|
|
const postgresUpdate = `
|
|
WITH latest AS (
|
|
SELECT target_event.id AS log_event_id, current_outbox.id, current_outbox.status
|
|
FROM logs_events AS target_event
|
|
JOIN LATERAL (
|
|
SELECT id, status
|
|
FROM logs_alert_outbox
|
|
WHERE log_event_id = target_event.id
|
|
ORDER BY id DESC
|
|
LIMIT 1
|
|
) AS current_outbox ON TRUE
|
|
WHERE target_event.dispatch_outbox_id = 0
|
|
)
|
|
UPDATE logs_events AS target_event
|
|
SET dispatch_outbox_id = latest.id,
|
|
dispatch_status = CASE latest.status WHEN 'processing' THEN 'retrying' ELSE latest.status END,
|
|
alert_sent = CASE WHEN latest.status = 'sent' THEN TRUE ELSE FALSE END
|
|
FROM latest
|
|
WHERE target_event.id = latest.log_event_id
|
|
AND target_event.dispatch_outbox_id = 0`
|
|
const mysqlUpdate = `
|
|
UPDATE logs_events AS target_event
|
|
JOIN (
|
|
SELECT candidate.id AS log_event_id, MAX(outbox.id) AS id
|
|
FROM logs_events AS candidate
|
|
JOIN logs_alert_outbox AS outbox ON outbox.log_event_id = candidate.id
|
|
WHERE candidate.dispatch_outbox_id = 0
|
|
GROUP BY candidate.id
|
|
) AS latest_id ON latest_id.log_event_id = target_event.id
|
|
JOIN logs_alert_outbox AS latest ON latest.id = latest_id.id
|
|
SET dispatch_outbox_id = latest.id,
|
|
dispatch_status = CASE latest.status WHEN 'processing' THEN 'retrying' ELSE latest.status END,
|
|
alert_sent = CASE WHEN latest.status = 'sent' THEN TRUE ELSE FALSE END
|
|
WHERE target_event.dispatch_outbox_id = 0`
|
|
|
|
switch db.Dialector.Name() {
|
|
case "postgres":
|
|
return db.Exec(postgresUpdate).Error
|
|
case "mysql":
|
|
return db.Exec(mysqlUpdate).Error
|
|
default:
|
|
return gorm.ErrUnsupportedDriver
|
|
}
|
|
}
|
|
|
|
func seedDefaultSyslogRules(db *gorm.DB) error {
|
|
rows := []SyslogRule{
|
|
{
|
|
Name: "默认-系统严重错误",
|
|
Enabled: true,
|
|
Priority: 100,
|
|
DeviceNameContains: "",
|
|
KeywordRegex: "(?i)(panic|fatal|segmentation fault|kernel panic|out of memory|oom)",
|
|
AlertName: "Syslog严重错误",
|
|
SeverityCode: "critical",
|
|
PolicyID: 0,
|
|
},
|
|
{
|
|
Name: "默认-链路中断告警",
|
|
Enabled: true,
|
|
Priority: 90,
|
|
DeviceNameContains: "",
|
|
KeywordRegex: "(?i)(link down|interface .* down|port .* down)",
|
|
SourceMatch: "",
|
|
MessageRegex: "(?i)(link down|interface .* down|port .* down|LINK_DOWN)",
|
|
AlertName: "Syslog链路中断",
|
|
SeverityCode: "major",
|
|
SeverityMappingJSON: `{"(?i)(critical|fatal|emergency)":"critical","(?i)(error|LINK_DOWN|down)":"major","(?i)(warning|warn)":"warning"}`,
|
|
ResourceUIDExtractRegex: `(?i)(?:resource_uid=|resource=)(?P<resource_uid>[a-z0-9_-]+:[a-z0-9_.:/-]+)|Interface (?P<iface>[A-Za-z0-9/._-]+)`,
|
|
PolicyID: 0,
|
|
},
|
|
{
|
|
Name: "H3C-Syslog-接口中断",
|
|
Enabled: true,
|
|
Priority: 120,
|
|
SourceMatch: "h3c",
|
|
MessageRegex: `(?i)(LINK_DOWN|Interface .* down|port .* down)`,
|
|
AlertName: "H3C Syslog接口中断",
|
|
SeverityCode: "major",
|
|
SeverityMappingJSON: `{"(?i)(LINK_DOWN|down)":"major","(?i)(LINK_UP|up)":"info"}`,
|
|
ResourceUIDExtractRegex: `(?i)(?:resource_uid=|resource=)(?P<resource_uid>network:[a-z0-9_.:/-]+)|Interface (?P<iface>[A-Za-z0-9/._-]+)`,
|
|
PolicyID: 0,
|
|
},
|
|
}
|
|
for _, row := range rows {
|
|
var existing SyslogRule
|
|
err := db.Where("name = ?", row.Name).First(&existing).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
if err := db.Create(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := db.Model(&existing).Omit("updated_at").Select(
|
|
"name",
|
|
"enabled",
|
|
"priority",
|
|
"device_name_contains",
|
|
"source_match",
|
|
"keyword_regex",
|
|
"message_regex",
|
|
"alert_name",
|
|
"severity_code",
|
|
"severity_mapping_json",
|
|
"resource_uid_extract_regex",
|
|
"policy_id",
|
|
).Updates(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func seedDefaultTrapRules(db *gorm.DB) error {
|
|
rows := []TrapRule{
|
|
{
|
|
Name: "默认-Trap链路中断",
|
|
Enabled: true,
|
|
Priority: 100,
|
|
OIDPrefix: "1.3.6.1.6.3.1.1.5",
|
|
VarbindMatchRegex: "(?i)(linkdown|ifdown|down)",
|
|
AlertName: "SNMP Trap链路中断",
|
|
SeverityCode: "major",
|
|
PolicyID: 0,
|
|
},
|
|
}
|
|
for _, row := range rows {
|
|
var existing TrapRule
|
|
err := db.Where("name = ?", row.Name).First(&existing).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
if err := db.Create(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := db.Model(&existing).Omit("updated_at").Select(
|
|
"name",
|
|
"enabled",
|
|
"priority",
|
|
"oid_prefix",
|
|
"varbind_match_regex",
|
|
"alert_name",
|
|
"severity_code",
|
|
"policy_id",
|
|
).Updates(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func seedDefaultTrapDictionary(db *gorm.DB) error {
|
|
rows := []TrapDictionaryEntry{
|
|
{
|
|
Vendor: "H3C",
|
|
OID: "1.3.6.1.6.3.1.1.5.3",
|
|
OIDPrefix: "1.3.6.1.6.3.1.1.5.3",
|
|
Name: "H3C ifDown 接口中断",
|
|
Title: "ifDown 接口中断",
|
|
Description: "检测到设备接口状态变为 down。",
|
|
SeverityCode: "major",
|
|
SeverityMappingJSON: `{"down":"major","up":"info"}`,
|
|
ParseExpression: `(?i)(ifName|interface)=?(?P<interface>[A-Za-z0-9/._-]+)`,
|
|
RecoveryMessage: "请检查链路、端口状态和对端设备。",
|
|
Enabled: true,
|
|
},
|
|
{
|
|
Vendor: "H3C",
|
|
OID: "1.3.6.1.6.3.1.1.5.4",
|
|
OIDPrefix: "1.3.6.1.6.3.1.1.5.4",
|
|
Name: "H3C ifUp 接口恢复",
|
|
Title: "ifUp 接口恢复",
|
|
Description: "检测到设备接口状态恢复为 up。",
|
|
SeverityCode: "info",
|
|
SeverityMappingJSON: `{"up":"info"}`,
|
|
ParseExpression: `(?i)(ifName|interface)=?(?P<interface>[A-Za-z0-9/._-]+)`,
|
|
RecoveryMessage: "接口已恢复,请确认业务连通性。",
|
|
Enabled: true,
|
|
},
|
|
}
|
|
for _, row := range rows {
|
|
var existing TrapDictionaryEntry
|
|
err := db.Where("o_id_prefix = ?", row.OIDPrefix).First(&existing).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
if err := db.Create(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := db.Model(&existing).Omit("updated_at").Select(
|
|
"o_id_prefix",
|
|
"vendor",
|
|
"o_id",
|
|
"name",
|
|
"title",
|
|
"description",
|
|
"severity_code",
|
|
"severity_mapping_json",
|
|
"parse_expression",
|
|
"recovery_message",
|
|
"enabled",
|
|
).Updates(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|