Files
logs/internal/models/query.go

262 lines
7.4 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).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).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("oid_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).Select(
"oid_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
}