package models import ( "bytes" "fmt" "html/template" "strconv" "strings" "github.com/didi/nightingale/v5/src/pkg/tplx" ) type AlertCurEvent struct { Id int64 `json:"id" gorm:"primaryKey"` Cluster string `json:"cluster"` GroupId int64 `json:"group_id"` // busi group id GroupName string `json:"group_name"` // busi group name Hash string `json:"hash"` // rule_id + vector_key RuleId int64 `json:"rule_id"` RuleName string `json:"rule_name"` RuleNote string `json:"rule_note"` RuleProd string `json:"rule_prod"` RuleAlgo string `json:"rule_algo"` Severity int `json:"severity"` PromForDuration int `json:"prom_for_duration"` PromQl string `json:"prom_ql"` PromEvalInterval int `json:"prom_eval_interval"` Callbacks string `json:"-"` // for db CallbacksJSON []string `json:"callbacks" gorm:"-"` // for fe RunbookUrl string `json:"runbook_url"` NotifyRecovered int `json:"notify_recovered"` NotifyChannels string `json:"-"` // for db NotifyChannelsJSON []string `json:"notify_channels" gorm:"-"` // for fe NotifyGroups string `json:"-"` // for db NotifyGroupsJSON []string `json:"notify_groups" gorm:"-"` // for fe NotifyGroupsObj []*UserGroup `json:"notify_groups_obj" gorm:"-"` // for fe TargetIdent string `json:"target_ident"` TargetNote string `json:"target_note"` TriggerTime int64 `json:"trigger_time"` TriggerValue string `json:"trigger_value"` Tags string `json:"-"` // for db TagsJSON []string `json:"tags" gorm:"-"` // for fe TagsMap map[string]string `json:"-" gorm:"-"` // for internal usage IsRecovered bool `json:"is_recovered" gorm:"-"` // for notify.py NotifyUsersObj []*User `json:"notify_users_obj" gorm:"-"` // for notify.py LastEvalTime int64 `json:"last_eval_time" gorm:"-"` // for notify.py 上次计算的时间 LastSentTime int64 `json:"last_sent_time" gorm:"-"` // 上次发送时间 NotifyCurNumber int `json:"notify_cur_number"` // notify: current number } func (e *AlertCurEvent) TableName() string { return "alert_cur_event" } func (e *AlertCurEvent) Add() error { return Insert(e) } type AggrRule struct { Type string Value string } func (e *AlertCurEvent) ParseRuleNote() error { e.RuleNote = strings.TrimSpace(e.RuleNote) if e.RuleNote == "" { return nil } var defs = []string{ "{{$labels := .TagsMap}}", "{{$value := .TriggerValue}}", } text := strings.Join(append(defs, e.RuleNote), "") t, err := template.New(fmt.Sprint(e.RuleId)).Funcs(tplx.TemplateFuncMap).Parse(text) if err != nil { return err } var body bytes.Buffer err = t.Execute(&body, e) if err != nil { return err } e.RuleNote = body.String() return nil } func (e *AlertCurEvent) GenCardTitle(rules []*AggrRule) string { arr := make([]string, len(rules)) for i := 0; i < len(rules); i++ { rule := rules[i] if rule.Type == "field" { arr[i] = e.GetField(rule.Value) } if rule.Type == "tagkey" { arr[i] = e.GetTagValue(rule.Value) } if len(arr[i]) == 0 { arr[i] = "Null" } } return strings.Join(arr, "::") } func (e *AlertCurEvent) GetTagValue(tagkey string) string { for _, tag := range e.TagsJSON { i := strings.Index(tag, tagkey+"=") if i >= 0 { return tag[len(tagkey+"="):] } } return "" } func (e *AlertCurEvent) GetField(field string) string { switch field { case "cluster": return e.Cluster case "group_id": return fmt.Sprint(e.GroupId) case "group_name": return e.GroupName case "rule_id": return fmt.Sprint(e.RuleId) case "rule_name": return e.RuleName case "severity": return fmt.Sprint(e.Severity) case "runbook_url": return e.RunbookUrl case "target_ident": return e.TargetIdent case "target_note": return e.TargetNote default: return "" } } func (e *AlertCurEvent) ToHis() *AlertHisEvent { isRecovered := 0 var recoverTime int64 = 0 if e.IsRecovered { isRecovered = 1 recoverTime = e.LastEvalTime } return &AlertHisEvent{ IsRecovered: isRecovered, Cluster: e.Cluster, GroupId: e.GroupId, GroupName: e.GroupName, Hash: e.Hash, RuleId: e.RuleId, RuleName: e.RuleName, RuleProd: e.RuleProd, RuleAlgo: e.RuleAlgo, RuleNote: e.RuleNote, Severity: e.Severity, PromForDuration: e.PromForDuration, PromQl: e.PromQl, PromEvalInterval: e.PromEvalInterval, Callbacks: e.Callbacks, RunbookUrl: e.RunbookUrl, NotifyRecovered: e.NotifyRecovered, NotifyChannels: e.NotifyChannels, NotifyGroups: e.NotifyGroups, TargetIdent: e.TargetIdent, TargetNote: e.TargetNote, TriggerTime: e.TriggerTime, TriggerValue: e.TriggerValue, Tags: e.Tags, RecoverTime: recoverTime, LastEvalTime: e.LastEvalTime, NotifyCurNumber: e.NotifyCurNumber, } } func (e *AlertCurEvent) DB2FE() { e.NotifyChannelsJSON = strings.Fields(e.NotifyChannels) e.NotifyGroupsJSON = strings.Fields(e.NotifyGroups) e.CallbacksJSON = strings.Fields(e.Callbacks) e.TagsJSON = strings.Split(e.Tags, ",,") } func (e *AlertCurEvent) DB2Mem() { e.IsRecovered = false e.NotifyGroupsJSON = strings.Fields(e.NotifyGroups) e.CallbacksJSON = strings.Fields(e.Callbacks) e.NotifyChannelsJSON = strings.Fields(e.NotifyChannels) e.TagsJSON = strings.Split(e.Tags, ",,") e.TagsMap = make(map[string]string) for i := 0; i < len(e.TagsJSON); i++ { pair := strings.TrimSpace(e.TagsJSON[i]) if pair == "" { continue } arr := strings.Split(pair, "=") if len(arr) != 2 { continue } e.TagsMap[arr[0]] = arr[1] } } // for webui func (e *AlertCurEvent) FillNotifyGroups(cache map[int64]*UserGroup) error { // some user-group already deleted ? count := len(e.NotifyGroupsJSON) if count == 0 { e.NotifyGroupsObj = []*UserGroup{} return nil } for i := range e.NotifyGroupsJSON { id, err := strconv.ParseInt(e.NotifyGroupsJSON[i], 10, 64) if err != nil { continue } ug, has := cache[id] if has { e.NotifyGroupsObj = append(e.NotifyGroupsObj, ug) continue } ug, err = UserGroupGetById(id) if err != nil { return err } if ug != nil { e.NotifyGroupsObj = append(e.NotifyGroupsObj, ug) cache[id] = ug } } return nil } func AlertCurEventTotal(prod string, bgid, stime, etime int64, severity int, clusters []string, query string) (int64, error) { session := DB().Model(&AlertCurEvent{}).Where("trigger_time between ? and ? and rule_prod = ?", stime, etime, prod) if bgid > 0 { session = session.Where("group_id = ?", bgid) } if severity >= 0 { session = session.Where("severity = ?", severity) } if len(clusters) > 0 { session = session.Where("cluster in ?", clusters) } if query != "" { arr := strings.Fields(query) for i := 0; i < len(arr); i++ { qarg := "%" + arr[i] + "%" session = session.Where("rule_name like ? or tags like ?", qarg, qarg) } } return Count(session) } func AlertCurEventGets(prod string, bgid, stime, etime int64, severity int, clusters []string, query string, limit, offset int) ([]AlertCurEvent, error) { session := DB().Where("trigger_time between ? and ? and rule_prod = ?", stime, etime, prod) if bgid > 0 { session = session.Where("group_id = ?", bgid) } if severity >= 0 { session = session.Where("severity = ?", severity) } if len(clusters) > 0 { session = session.Where("cluster in ?", clusters) } if query != "" { arr := strings.Fields(query) for i := 0; i < len(arr); i++ { qarg := "%" + arr[i] + "%" session = session.Where("rule_name like ? or tags like ?", qarg, qarg) } } var lst []AlertCurEvent err := session.Order("id desc").Limit(limit).Offset(offset).Find(&lst).Error if err == nil { for i := 0; i < len(lst); i++ { lst[i].DB2FE() } } return lst, err } func AlertCurEventDel(ids []int64) error { if len(ids) == 0 { return nil } return DB().Where("id in ?", ids).Delete(&AlertCurEvent{}).Error } func AlertCurEventDelByHash(hash string) error { return DB().Where("hash = ?", hash).Delete(&AlertCurEvent{}).Error } func AlertCurEventExists(where string, args ...interface{}) (bool, error) { return Exists(DB().Model(&AlertCurEvent{}).Where(where, args...)) } func AlertCurEventGet(where string, args ...interface{}) (*AlertCurEvent, error) { var lst []*AlertCurEvent err := DB().Where(where, args...).Find(&lst).Error if err != nil { return nil, err } if len(lst) == 0 { return nil, nil } lst[0].DB2FE() lst[0].FillNotifyGroups(make(map[int64]*UserGroup)) return lst[0], nil } func AlertCurEventGetById(id int64) (*AlertCurEvent, error) { return AlertCurEventGet("id=?", id) } type AlertNumber struct { GroupId int64 GroupCount int64 } // for busi_group list page func AlertNumbers(bgids []int64) (map[int64]int64, error) { ret := make(map[int64]int64) if len(bgids) == 0 { return ret, nil } var arr []AlertNumber err := DB().Model(&AlertCurEvent{}).Select("group_id", "count(*) as group_count").Where("group_id in ?", bgids).Group("group_id").Find(&arr).Error if err != nil { return nil, err } for i := 0; i < len(arr); i++ { ret[arr[i].GroupId] = arr[i].GroupCount } return ret, nil } func AlertCurEventGetAll(cluster string) ([]*AlertCurEvent, error) { session := DB().Model(&AlertCurEvent{}) if cluster != "" { session = session.Where("cluster = ?", cluster) } var lst []*AlertCurEvent err := session.Find(&lst).Error return lst, err } func AlertCurEventGetByIds(ids []int64) ([]*AlertCurEvent, error) { var lst []*AlertCurEvent if len(ids) == 0 { return lst, nil } err := DB().Where("id in ?", ids).Order("id desc").Find(&lst).Error if err == nil { for i := 0; i < len(lst); i++ { lst[i].DB2FE() } } return lst, err } func AlertCurEventGetByRule(ruleId int64) ([]*AlertCurEvent, error) { var lst []*AlertCurEvent err := DB().Where("rule_id=?", ruleId).Find(&lst).Error return lst, err } func AlertCurEventGetMap(cluster string) (map[int64]map[string]struct{}, error) { session := DB().Model(&AlertCurEvent{}) if cluster != "" { session = session.Where("cluster = ?", cluster) } var lst []*AlertCurEvent err := session.Select("rule_id", "hash").Find(&lst).Error if err != nil { return nil, err } ret := make(map[int64]map[string]struct{}) for i := 0; i < len(lst); i++ { rid := lst[i].RuleId hash := lst[i].Hash if _, has := ret[rid]; has { ret[rid][hash] = struct{}{} } else { ret[rid] = make(map[string]struct{}) ret[rid][hash] = struct{}{} } } return ret, nil }