diff --git a/src/bean/entity/alert.go b/src/bean/entity/alert.go index ddad854623648c15c391cdf4db3befac5c4edb9d..0c93278422d4ad417de7289c98f513e01d55bf59 100644 --- a/src/bean/entity/alert.go +++ b/src/bean/entity/alert.go @@ -4,41 +4,41 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" ) -// Alert 预警表 +// Alert 预警 type Alert struct { - Id int `json:"id"` // 预警列表ID,主键,自增长 - AlertPoint string `json:"alert_point"` // 预警点 + Id int `json:"id"` // 预警id + AlertPoint string `json:"alert_point"` // 预警点:预警分类/预警规则名称 ClassParentName/AlertRulesName AlertRulesId string `json:"alert_rules_id"` // 告警规则id + AlertRulesName string `json:"alert_rules_name"` // 告警规则名称 RiskLevel int `json:"risk_level"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 AlertTime jsontime.Time `json:"alert_time"` // 预警时间 ClassId int `json:"class_id"` // 预警对象id(级联:预警分类/预警对象) ClassParentName string `json:"class_parent_name"` // 预警分类名称 TODO 该字段做关联存储或查询 ClassName string `json:"class_name"` // 预警对象名称 TODO 该字段做关联存储或查询 - MetricConfigId string `json:"metric_config_id"` // 预警指标id // 预警指标 + MetricConfigId string `json:"metric_config_id"` // 预警指标id MetricConfigName string `json:"metric_config_name"` // 预警指标名称 AlertRuleType string `json:"alert_rule_type"` // 预警规则类型id TODO 该字段做关联存储或查询 AlertRuleTypeName string `json:"alert_rule_type_name"` // 预警规则类型名称 TODO 该字段做关联存储或查询 CurrentValue float64 `json:"current_value"` // 当前报警值 - AlertCondition AlertCondition `json:"alert_condition" xorm:"alert_condition"` - NotificationCount int `json:"notification_count"` // 通知人数 - PushCount int `json:"push_count"` // 推送次数 - LastPushTime jsontime.Time `json:"last_push_time"` // 最近推送时间 - Status int `json:"status"` // 状态,1:已恢复 2:未恢复 3:已关闭 - PushRecords []PushRecord `json:"push_records"` - DisposedList []DisposedList `json:"disposed_list"` // 处置列表 - IsDisposed int `json:"is_disposed"` // 是否处置(工单管理),1:已处置,2:未处置 通过DisposedList逻辑处理回显 - CloseRemark string `json:"close_remark"` // 关闭备注(预警关闭提醒) - CloseUser string `json:"close_user"` // 关闭用户,预警关闭提醒 - CloseTime jsontime.Time `json:"close_time"` // 关闭关闭时间,预警关闭提醒 - DeferPush int `json:"defer_push" xorm:"defer_push"` // 延迟三天推送: 0:否 1:是 三天内将不再自动推送该告警信息给处置人员,可手动推送,但告警数据依然会出现 - - CreatedBy string `json:"created_by" xorm:"'created_by'"` // 创建人 - CreatedAt jsontime.Time `json:"created_at" xorm:"'created_at'"` // 创建时间 - UpdatedBy string `json:"updated_by" xorm:"'updated_by'"` // 更新人 - UpdatedAt jsontime.Time `json:"updated_at" xorm:"'updated_at'"` // 更新时间 + AlertCondition AlertCondition `json:"alert_condition"` // 预警范围 + NotificationCount int `json:"notification_count"` // 通知人数 + PushCount int `json:"push_count"` // 推送次数 + LastPushTime jsontime.Time `json:"last_push_time"` // 最近推送时间 + Status int `json:"status"` // 状态,1:已恢复 2:未恢复 3:已关闭 + PushRecords []PushRecord `json:"push_records"` // 推送记录列表 + DisposedList []Disposed `json:"disposed_list"` // 处置列表 + IsDisposed int `json:"is_disposed"` // 是否处置(工单管理),1:已处置,2:未处置 通过DisposedList逻辑处理回显 + CloseRemark string `json:"close_remark"` // 关闭备注(预警关闭提醒) + CloseUser string `json:"close_user"` // 关闭用户,预警关闭提醒 + CloseTime jsontime.Time `json:"close_time"` // 关闭关闭时间,预警关闭提醒 + DeferPush int `json:"defer_push"` // 延迟三天推送: 0:否 1:是 三天内将不再自动推送该告警信息给处置人员,可手动推送,但告警数据依然会出现 + CreatedBy string `json:"created_by"` // 创建人 + CreatedAt jsontime.Time `json:"created_at"` // 创建时间 + UpdatedBy string `json:"updated_by"` // 更新人 + UpdatedAt jsontime.Time `json:"updated_at"` // 更新时间 } -type DisposedList struct { +type Disposed struct { IsDisposed int `json:"is_disposed"` // 是否处置(工单管理),1:已处置,2:未处置 DisposalContent string `json:"disposal_content"` // 处置内容(工单管理,结果反馈) DisposalUser string `json:"disposal_user"` // 处置人(工单管理,结果反馈) diff --git a/src/bean/vo/request/alert.go b/src/bean/vo/request/alert.go index cc23a28dfa8cf8fb6fab55606925f3eee53f58e5..c8f8cc5b317a62444dd1853f73b3381c08ba36a7 100644 --- a/src/bean/vo/request/alert.go +++ b/src/bean/vo/request/alert.go @@ -9,13 +9,14 @@ type DetailAlert struct { } type ListAlert struct { - Id int `json:"id" form:"id"` - Ids []int `json:"ids" form:"ids"` // 预警ids - RiskLevel int `json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 - Status int `json:"status" form:"status" binding:"omitempty,oneof=1 2 3"` // 状态,1:已恢复 2:未恢复 3:已关闭 - Keyword string `json:"keyword" form:"keyword"` // 预警点/分类/指标 - StartTime string `json:"start_time" form:"start_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` - EndTime string `json:"end_time" form:"end_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` + Id int `json:"id" form:"id"` + Ids []int `json:"ids" form:"ids"` // 预警ids + AlertRulesId string `json:"alert_rules_id" form:"alert_rules_id"` // 告警规则id + RiskLevel int `json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 + Status int `json:"status" form:"status" binding:"omitempty,oneof=1 2 3"` // 状态,1:已恢复 2:未恢复 3:已关闭 + Keyword string `json:"keyword" form:"keyword"` // 预警点/分类/指标 + StartTime string `json:"start_time" form:"start_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` + EndTime string `json:"end_time" form:"end_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` Pagination } @@ -24,13 +25,19 @@ type ExistAlert struct { AlertRulesId string `json:"alert_rules_id" form:"'alert_rules_id'"` // 告警规则id } +type CreateAlert struct { + entity.Alert +} + type UpdateAlert struct { Id int `json:"id" form:"id" binding:"required"` CloseRemark string `json:"close_remark" form:"close_remark"` // 关闭备注 DeferPush int `json:"defer_push" form:"defer_push" binding:"omitempty,oneof=0 1"` // 延迟三天推送: 0:否 1:是 三天内将不再自动推送该告警信息给处置人员,可手动推送,但告警数据依然会出现 RiskLevel int `json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 Status int `json:"status" form:"status" binding:"omitempty,oneof=1 2 3"` // 状态,1:已恢复 2:未恢复 3:已关闭 + CurrentValue float64 `json:"current_value" form:"current_value"` // 当前报警值 DisposalContent string `json:"disposal_content" form:"disposal_content"` // 处置内容(工单管理,结果反馈) + PushCount int `json:"push_count" form:"push_count"` // 推送次数 PushRecords []entity.PushRecord `json:"push_records" form:"push_records"` } diff --git a/src/bean/vo/response/alert.go b/src/bean/vo/response/alert.go index cdc17ba8d6010d3b95af4454dd979841c540632d..5c729b01921c483d2ee7b491aea6bde1b13df0ae 100644 --- a/src/bean/vo/response/alert.go +++ b/src/bean/vo/response/alert.go @@ -26,6 +26,12 @@ type CatIndices struct { PriStoreSize string `json:"pri.store.size"` } +type CatIndexCount struct { + Epoch string `json:"epoch"` + Timestamp string `json:"timestamp"` + Count string `json:"count"` +} + type OpenSearchSource struct { Took int `json:"took"` TimedOut bool `json:"timed_out"` diff --git a/src/bean/vo/response/alert_rules.go b/src/bean/vo/response/alert_rules.go index a64bcc19724208bc28cd161c9b94a9aebd98bcb3..a4dea8e4ca66fd8fcd019b507264c2b3146f7f9a 100644 --- a/src/bean/vo/response/alert_rules.go +++ b/src/bean/vo/response/alert_rules.go @@ -5,7 +5,8 @@ import "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" type AlertRulesItem struct { entity.AlertRules `xorm:"extends"` Expr string `json:"expr" form:"expr"` // 指标表达式(PromQL语句) - AlertRuleType string `json:"alert_rule_type" xorm:"alert_rule_type"` // 预警规则类型 关联字典表 + AlertRuleType string `json:"alert_rule_type" xorm:"alert_rule_type"` // 预警规则类型 + AlertRuleTypeName string `json:"alert_rule_type_name"` // 预警规则类型名称 ClassParentName string `json:"class_parent_name" xorm:"class_parent_name"` ClassName string `json:"class_name" xorm:"class_name"` MetricConfigName string `json:"metric_config_name" xorm:"metric_config_name"` diff --git a/src/controller/alert_webhook.go b/src/controller/alert_webhook.go index 6e7f78746fad8d4bb52b1a9ea2842b4427413b90..6b7de1c595f3b9edea2fa5e7e8e11054003e7036 100644 --- a/src/controller/alert_webhook.go +++ b/src/controller/alert_webhook.go @@ -3,39 +3,32 @@ package controller import ( "github.com/gin-gonic/gin" "github.com/prometheus/alertmanager/notify/webhook" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" "go.uber.org/zap" ) // AlertWebhook 回调 func AlertWebhook(c *gin.Context) { - // 读取请求体原始数据 var req webhook.Message if err := c.ShouldBind(&req); err != nil { SendJsonResponse(c, resp.InvalidParam.TranslateError(err), nil) return } - - /*bodyBytes, err := io.ReadAll(c.Request.Body) + conf.Logger.Info("------>webhook.Message------>", zap.Any("message", req)) + svc := service.AlertWebhookSvc{User: header.GetUser(c)} + db, err := client.GetDbClient() if err != nil { - // 处理错误 - c.String(500, "Error reading request body") + SendJsonResponse(c, resp.DbConnectError.WithError(err), nil) return - }*/ - - // 输出请求体原始数据到控制台 - //log.Printf("原始数据------>\n%s\n<------", string(bodyBytes)) - - // 将请求体数据重新放入请求中,以便后续处理 - //c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - - conf.Logger.Info("------>webhook.Message------>", zap.Any("message", req)) - - /*var payload map[string]interface{} - if err := c.ShouldBind(&payload); err != nil { - log.Println("Failed to parse JSON: ", err) - SendJsonResponse(c, err, "") - }*/ + } + err = svc.AlertWebhook(db.NewSession(), req) + if err != nil { + SendJsonResponse(c, resp.FAIL.WithError(err), nil) + return + } SendJsonResponse(c, resp.OK, req) } diff --git a/src/pkg/beagle/constant/constant.go b/src/pkg/beagle/constant/constant.go index 2926a8458987c4c065c92c919ea52a094d60726a..65d58f14d412aff2c02502fd3cec902e8b8b3b34 100644 --- a/src/pkg/beagle/constant/constant.go +++ b/src/pkg/beagle/constant/constant.go @@ -145,3 +145,10 @@ func RiskLeveText(code int) string { return "" } } + +// 预警状态,1:已恢复 2:未恢复 3:已关闭 +const ( + AlertRecovered = iota + 1 + AlertNotRecovered + AlertClosed +) diff --git a/src/service/alert.go b/src/service/alert.go index e725a4a7be52f17ba4d49d070bd2f8d8d3b80853..73f59f5567268fd4ff3932d47174459923d0e94f 100644 --- a/src/service/alert.go +++ b/src/service/alert.go @@ -14,6 +14,7 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "go.uber.org/zap" "io" @@ -52,6 +53,15 @@ var ( "alert_rules_id": { "type": "keyword" }, + "alert_rules_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, "risk_level": { "type": "integer" }, @@ -324,7 +334,7 @@ func (a *AlertSvc) Indices() (indices []string, err error) { return } defer do.Body.Close() - if do.StatusCode != http.StatusOK { + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { err = errors.New(do.String()) return } @@ -346,7 +356,7 @@ func (a *AlertSvc) Indices() (indices []string, err error) { return } -func (a *AlertSvc) IndexSearch(req request.ListAlert) (resp response.AlertList, err error) { +func (a *AlertSvc) DocSearch(req request.ListAlert) (resp response.AlertList, err error) { var ( sources response.OpenSearchSource ) @@ -362,6 +372,10 @@ func (a *AlertSvc) IndexSearch(req request.ListAlert) (resp response.AlertList, boolQuery.Must(elastic.NewMatchQuery("id", req.Id)) } + if req.AlertRulesId != "" { + boolQuery.Must(elastic.NewMatchQuery("alert_rules_id", req.AlertRulesId)) + } + if req.RiskLevel != 0 { boolQuery.Must(elastic.NewMatchQuery("risk_level", req.RiskLevel)) } @@ -417,7 +431,7 @@ func (a *AlertSvc) IndexSearch(req request.ListAlert) (resp response.AlertList, return } defer do.Body.Close() - if do.StatusCode != http.StatusOK { + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { err = errors.New(do.String()) return } @@ -473,7 +487,7 @@ func (a *AlertSvc) IndexDocExist(req request.ExistAlert) (exist bool, err error) return } defer do.Body.Close() - if do.StatusCode != http.StatusOK { + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { err = errors.New(do.String()) return } @@ -493,7 +507,97 @@ func (a *AlertSvc) IndexDocExist(req request.ExistAlert) (exist bool, err error) return } -func (a *AlertSvc) IndexUpdate(req request.UpdateAlert) (err error) { +func (a *AlertSvc) CatCount(indexName ...string) (count int) { + var ( + list []response.CatIndexCount + index string + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if len(indexName) > 0 && indexName[0] != "" { + index = indexName[0] + "*" + } else { + index = OpenSearchIndex + "*" + } + + res := opensearchapi.CatCountRequest{ + Index: []string{index}, + Format: "json", + } + do, err := res.Do(context.Background(), cli) + if err != nil { + return + } + defer do.Body.Close() + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { + err = errors.New(do.String()) + return + } + body, err := io.ReadAll(do.Body) + if err != nil { + return + } + err = json.Unmarshal(body, &list) + if err != nil { + return + } + if len(list) > 0 { + count = cast.ToInt(list[0].Count) + } + return +} + +func (a *AlertSvc) DocCreate(req request.CreateAlert) (err error) { + var ( + sources response.OpenSearchSource + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + b, _ := json.Marshal(&req.Alert) + + docStr := string(b) + if docStr == "" { + return errors.New("doc marshal failed") + } + content := strings.NewReader(fmt.Sprintf(`%s`, docStr)) + + res := opensearchapi.CreateRequest{ + Index: OpenSearchIndex, + DocumentID: cast.ToString(req.Id), + Body: content, + } + do, err := res.Do(context.Background(), cli) + if err != nil { + return + } + defer do.Body.Close() + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { + err = errors.New(do.String()) + return + } + body, err := io.ReadAll(do.Body) + if err != nil { + return + } + + err = json.Unmarshal(body, &sources) + if err != nil { + return + } + + conf.Logger.Info("--->alert create--->", zap.Any("DocCreate", sources)) + return +} + +func (a *AlertSvc) DocUpdate(req request.UpdateAlert) (err error) { var ( sources response.OpenSearchSource now = jsontime.Now() @@ -517,23 +621,15 @@ func (a *AlertSvc) IndexUpdate(req request.UpdateAlert) (err error) { if req.Status != 0 { doc["status"] = req.Status } - doc["updated_at"] = now - - if req.DisposalContent != "" { - var detailAlert response.AlertItem - detailAlert, err = a.GetDataById(request.DetailAlert{Id: req.Id}) - if err != nil { - return err - } - disposedList := detailAlert.DisposedList - disposedList = append(disposedList, entity.DisposedList{ - IsDisposed: req.Status, - DisposalContent: req.DisposalContent, - DisposalUser: a.User.SystemAccount, - DisposalTime: now, - }) - doc["disposed_list"] = disposedList + if req.CurrentValue != 0 { + doc["current_value"] = req.CurrentValue } + /*if req.CurrentValue != 0 { + doc["current_value"] = req.CurrentValue + }*/ + + doc["updated_by"] = a.User.SystemAccount + doc["updated_at"] = now if len(req.PushRecords) > 0 { doc["push_records"] = req.PushRecords @@ -564,7 +660,7 @@ func (a *AlertSvc) IndexUpdate(req request.UpdateAlert) (err error) { return } defer do.Body.Close() - if do.StatusCode != http.StatusOK { + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { err = errors.New(do.String()) return } @@ -578,18 +674,38 @@ func (a *AlertSvc) IndexUpdate(req request.UpdateAlert) (err error) { return } - conf.Logger.Info("--->alert update--->", zap.Any("IndexUpdate", sources)) + conf.Logger.Info("--->alert update--->", zap.Any("DocUpdate", sources)) return } +func (a *AlertSvc) Create() error { + cli, err := client.GetOpenSearch() + if err != nil { + return err + } + res := opensearchapi.IndicesCreateRequest{ + Index: OpenSearchIndex, + Body: Mapping, + } + do, err := res.Do(context.Background(), cli) + if err != nil { + return err + } + defer do.Body.Close() + if do.StatusCode != http.StatusOK { + return errors.New(do.String()) + } + return nil +} + func (a *AlertSvc) Update(req request.UpdateAlert) error { - err := a.IndexUpdate(req) + err := a.DocUpdate(req) return err } func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { now := jsontime.Now() - resp, err := a.IndexSearch(request.ListAlert{Ids: req.Ids}) + resp, err := a.DocSearch(request.ListAlert{Ids: req.Ids}) if err != nil { return } @@ -619,7 +735,7 @@ func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { pushRecords = append(pushRecords, data) } - err = a.IndexUpdate(request.UpdateAlert{ + err = a.DocUpdate(request.UpdateAlert{ Id: alert.Id, PushRecords: pushRecords, }) @@ -642,7 +758,7 @@ func (a *AlertSvc) BatchCloseAlert(req request.BatchCloseAlert) (err error) { ids = append(ids, req.Id) } for _, id := range ids { - err = a.IndexUpdate(request.UpdateAlert{ + err = a.DocUpdate(request.UpdateAlert{ Id: id, CloseRemark: req.CloseRemark, DeferPush: req.DeferPush, @@ -658,15 +774,26 @@ func (a *AlertSvc) BatchCloseAlert(req request.BatchCloseAlert) (err error) { } func (a *AlertSvc) GetDataById(req request.DetailAlert) (resp response.AlertItem, err error) { - list, err := a.IndexSearch(request.ListAlert{Id: req.Id}) + list, err := a.DocSearch(request.ListAlert{Id: req.Id}) if len(list.List) > 0 { resp = list.List[0] } return } +func (a *AlertSvc) GetDataByAlertRulesIdAndRiskLevel(alertRulesId string, riskLevel int, status int) (resp response.AlertItem, err error) { + list, err := a.DocSearch(request.ListAlert{AlertRulesId: alertRulesId, RiskLevel: riskLevel, Status: status}) + if len(list.List) > 0 { + resp = list.List[0] + } + /*if resp.Id == 0 { + err = errors.New("no data found") + }*/ + return +} + func (a *AlertSvc) List(req request.ListAlert) (resp response.AlertList, err error) { - resp, err = a.IndexSearch(req) + resp, err = a.DocSearch(req) resp.TotalCount = int64(len(resp.List)) return } @@ -684,10 +811,10 @@ func (a *AlertSvc) DisposeAlert(req request.DisposeAlert) (err error) { return err } - if detailAlert.Status == 1 { + if detailAlert.Status == constant.AlertRecovered { return errors.New("alert has been restored") } - if detailAlert.Status == 3 { + if detailAlert.Status == constant.AlertClosed { return errors.New("alert has been closed") } @@ -700,7 +827,7 @@ func (a *AlertSvc) DisposeAlert(req request.DisposeAlert) (err error) { doc["status"] = req.Status if req.DisposalContent != "" { disposedList := detailAlert.DisposedList - disposedList = append(disposedList, entity.DisposedList{ + disposedList = append(disposedList, entity.Disposed{ IsDisposed: req.Status, DisposalContent: req.DisposalContent, DisposalUser: a.User.SystemAccount, @@ -730,7 +857,7 @@ func (a *AlertSvc) DisposeAlert(req request.DisposeAlert) (err error) { return } defer do.Body.Close() - if do.StatusCode != http.StatusOK { + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { err = errors.New(do.String()) return } diff --git a/src/service/alert_rules.go b/src/service/alert_rules.go index bef47322da9b9c5a74b65ff500b741debca4221e..32b89d35711e6a30e8e156c303402678d610bf63 100644 --- a/src/service/alert_rules.go +++ b/src/service/alert_rules.go @@ -279,10 +279,11 @@ func (a *AlertRulesSvc) List(req request.ListAlertRules) (resp response.AlertRul } session := db.NewSession() defer session.Close() - session.Table(new(entity.AlertRules)).Alias("r").Select("r.*,acp.class_name class_parent_name,ac.class_name,mc.expr,mc.metric_name metric_config_name,mc.alert_rule_type") + session.Table(new(entity.AlertRules)).Alias("r").Select("r.*,acp.class_name class_parent_name,ac.class_name,mc.expr,mc.metric_name metric_config_name,mc.alert_rule_type,dict.name alert_rule_type_name") session.Join("LEFT", "metric_config mc", "mc.id = r.metric_config_id") session.Join("LEFT", "alert_class ac", "ac.class_id = r.class_id") session.Join("LEFT", "alert_class acp", "acp.class_id = ac.parent_id") + session.Join("LEFT", "dict", "dict.id = mc.alert_rule_type") if req.Id != "" { session.Where("r.id = ?", req.Id) } diff --git a/src/service/alert_webhook.go b/src/service/alert_webhook.go index b064432ba579b5c12cd7343d60ceb3e85a55a054..b77d81e95d43877d8e77849bf8a8b8e306d7b9f5 100644 --- a/src/service/alert_webhook.go +++ b/src/service/alert_webhook.go @@ -1,8 +1,14 @@ package service import ( + "errors" "github.com/prometheus/alertmanager/notify/webhook" + "github.com/spf13/cast" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "xorm.io/xorm" ) @@ -10,10 +16,116 @@ type AlertWebhookSvc struct { User entity.SystemUserInfo } -// TODO AlertManager 回调接口 -func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Message) (id string, err error) { - // 1.判断该opensearch数据是否恢复 - // 1.1.如果恢复,则查询当前opensearch最大id值,插入新的数据到预警列表 - // 1.2如果未恢复,根据id更新该条数据 +// AlertWebhook Alertmanager 回调接口 +func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Message) (err error) { + now := jsontime.Now() + alertSvc := AlertSvc{User: a.User} + alertRulesSvc := AlertRulesSvc{User: a.User} + + for _, alert := range req.Alerts { + var ( + alertRulesId string + riskLevelStr string + riskLevel int + currentValueStr string + currentValue float64 + ok bool + alertItem response.AlertItem + alertRulesItem response.AlertRulesItem + alertStatus int + alertCondition entity.AlertCondition + ) + + if alertRulesId, ok = alert.Labels["alert_rules_id"]; !ok { + return errors.New("alert_rules_id not found in the map") + } + + if riskLevelStr, ok = alert.Labels["risk_level"]; !ok { + return errors.New("risk_level not found in the map") + } + riskLevel = cast.ToInt(riskLevelStr) + + if currentValueStr, ok = alert.Annotations["value"]; !ok { + return errors.New("value not found in the map") + } + currentValue = cast.ToFloat64(currentValueStr) + + alertRulesItem, err = alertRulesSvc.GetDataById(request.DetailAlertRules{Id: alertRulesId}) + if err != nil { + return + } + + alertItem, err = alertSvc.GetDataByAlertRulesIdAndRiskLevel(alertRulesId, riskLevel, 2) + if err != nil { + return + } + + var newData bool + if alertItem.Id == 0 { // 新数据 + newData = true + } + + switch newData { + case true: // 新增数据到OpenSearch + + max := alertSvc.CatCount(OpenSearchIndex) + if max == 0 { + return + } + alertId := max + 1 + for _, v := range alertRulesItem.AlertCondition { + if v.RiskLevel == riskLevel { + alertCondition = v + break + } + } + err = alertSvc.DocCreate(request.CreateAlert{Alert: entity.Alert{ + Id: alertId, + AlertPoint: alertRulesItem.ClassParentName + "/" + alertRulesItem.MetricName, + AlertRulesId: alertRulesItem.Id, + AlertRulesName: alertRulesItem.MetricName, + RiskLevel: riskLevel, + AlertTime: jsontime.Time(alert.StartsAt), + ClassId: alertRulesItem.ClassId, + ClassParentName: alertRulesItem.ClassParentName, + ClassName: alertRulesItem.ClassName, + MetricConfigId: alertRulesItem.MetricConfigId, + MetricConfigName: alertRulesItem.MetricConfigName, + AlertRuleType: alertRulesItem.AlertRuleType, + AlertRuleTypeName: alertRulesItem.AlertRuleTypeName, + CurrentValue: currentValue, + AlertCondition: alertCondition, + NotificationCount: len(alertRulesItem.NotifyRecipients), + PushCount: 1, + LastPushTime: now, + Status: constant.AlertNotRecovered, + CreatedBy: a.User.SystemAccount, + CreatedAt: now, + UpdatedBy: a.User.SystemAccount, + UpdatedAt: now, + }}) + + if err != nil { + return + } + + default: // 旧数据,更新或解决 + + switch alert.Status { + case "firing": + alertStatus = constant.AlertNotRecovered + case "resolved": + alertStatus = constant.AlertRecovered + } + + err = alertSvc.Update(request.UpdateAlert{ + Id: alertItem.Id, + CurrentValue: currentValue, + RiskLevel: riskLevel, + Status: alertStatus, + }) + } + } + return }