diff --git a/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@cn/DEFAULT b/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@cn/DEFAULT index f1f6409560..0d444f4ac4 100644 --- a/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@cn/DEFAULT +++ b/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@cn/DEFAULT @@ -2,7 +2,7 @@ 策略名称: {{.name}} 触发时间: {{.start_time}} 报警级别: {{.level}} -触发条件: {{.description}} +触发条件: {{.description | unescaped}} 资源数量:{{len .matches}} 资源名称:{{.resource_name}} diff --git a/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@en/DEFAULT b/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@en/DEFAULT index 0ee07a2ae4..0cfd74b27b 100644 --- a/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@en/DEFAULT +++ b/build/monitor/root/opt/yunion/share/notify_templates/alerter/content@en/DEFAULT @@ -2,7 +2,7 @@ AlertName: {{.name}} Time: {{.start_time}} Level: {{.level}} -TriggerCondition: {{html .description}} +TriggerCondition: {{.description | unescaped}} ResourceCount: {{len .matches}} ResourceName: {{.resource_name}} diff --git a/pkg/apis/monitor/alertrecord.go b/pkg/apis/monitor/alertrecord.go index c76ca36313..1d5b0f5991 100644 --- a/pkg/apis/monitor/alertrecord.go +++ b/pkg/apis/monitor/alertrecord.go @@ -34,7 +34,9 @@ type AlertRecordCreateInput struct { type AlertRecordRule struct { Metric string `json:"metric"` + Measurement string `json:"measurement"` MeasurementDesc string `json:"measurement_desc"` + Field string `json:"field"` FieldDesc string `json:"field_desc"` // 比较运算符, 比如: >, <, >=, <= Comparator string `json:"comparator"` diff --git a/pkg/cloudcommon/notifyclient/notify.go b/pkg/cloudcommon/notifyclient/notify.go index d5ec46313d..1c23a1fec2 100644 --- a/pkg/cloudcommon/notifyclient/notify.go +++ b/pkg/cloudcommon/notifyclient/notify.go @@ -91,7 +91,9 @@ func getTemplate(ctx context.Context, topic string, contType string, channel npk if err != nil { return nil, err } - tmp, err := template.New(key).Parse(string(cont)) + tmp := template.New(key) + tmp.Funcs(template.FuncMap{"unescaped": unescaped}) + tmp, err = tmp.Parse(string(cont)) if err != nil { return nil, err } @@ -100,6 +102,10 @@ func getTemplate(ctx context.Context, topic string, contType string, channel npk return templatesTable[key], nil } +func unescaped(str string) template.HTML { + return template.HTML(str) +} + func getContent(ctx context.Context, topic string, contType string, channel npk.TNotifyChannel, data jsonutils.JSONObject) (string, error) { if channel == npk.NotifyByWebhook { return "", nil diff --git a/pkg/monitor/alerting/conditions/query.go b/pkg/monitor/alerting/conditions/query.go index 39959853ee..967679ab15 100644 --- a/pkg/monitor/alerting/conditions/query.go +++ b/pkg/monitor/alerting/conditions/query.go @@ -223,11 +223,25 @@ func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb if alertDetails.GetPointStr { evalMatch.ValueStr = c.jointPointStr(series, evalMatch.ValueStr, valStrArr) } - evalMatch.MeasurementDesc = alertDetails.MeasurementDisplayName - evalMatch.FieldDesc = alertDetails.FieldDescription.DisplayName + c.newRuleDescription(context, alertDetails) return evalMatch, nil } +func (c *QueryCondition) newRuleDescription(context *alerting.EvalContext, alertDetails *monitor.CommonAlertMetricDetails) { + ruleDes := alerting.RuleDescription{ + AlertRecordRule: monitor.AlertRecordRule{ + Metric: fmt.Sprintf("%s.%s", alertDetails.Measurement, alertDetails.Field), + Measurement: alertDetails.Measurement, + MeasurementDesc: alertDetails.MeasurementDisplayName, + Field: alertDetails.Field, + FieldDesc: alertDetails.FieldDescription.DisplayName, + Comparator: alertDetails.Comparator, + Threshold: c.RationalizeValueFromUnit(alertDetails.Threshold, alertDetails.FieldDescription.Unit, ""), + }, + } + context.RuleDescription = &ruleDes +} + func (c *QueryCondition) jointPointStr(series tsdb.TimeSeries, value string, valStrArr []string) string { str := "" for i := 0; i < len(valStrArr); i++ { diff --git a/pkg/monitor/alerting/eval_context.go b/pkg/monitor/alerting/eval_context.go index 83472ade9a..9845e8f823 100644 --- a/pkg/monitor/alerting/eval_context.go +++ b/pkg/monitor/alerting/eval_context.go @@ -42,6 +42,7 @@ type EvalContext struct { StartTime time.Time EndTime time.Time Rule *Rule + RuleDescription *RuleDescription NoDataFound bool PrevAlertState monitor.AlertStateType @@ -50,6 +51,10 @@ type EvalContext struct { UserCred mcclient.TokenCredential } +type RuleDescription struct { + monitor.AlertRecordRule +} + // NewEvalContext is the EvalContext constructor. func NewEvalContext(alertCtx context.Context, userCred mcclient.TokenCredential, rule *Rule) *EvalContext { return &EvalContext{ diff --git a/pkg/monitor/alerting/notifier.go b/pkg/monitor/alerting/notifier.go index d3753b5cea..f5cd2eff41 100644 --- a/pkg/monitor/alerting/notifier.go +++ b/pkg/monitor/alerting/notifier.go @@ -17,7 +17,6 @@ package alerting import ( "database/sql" "fmt" - "strings" "time" "yunion.io/x/jsonutils" @@ -143,32 +142,47 @@ func (n *notificationService) getNeededNotifiers(nIds []string, evalCtx *EvalCon } } if shouldNotify { - var matches []*monitor.EvalMatch - if evalCtx.Firing { - matches = evalCtx.EvalMatches - } else { - matches = evalCtx.AlertOkEvalMatches - } - recordCreateInput := monitor.AlertRecordCreateInput{ - StandaloneResourceCreateInput: apis.StandaloneResourceCreateInput{ - GenerateName: evalCtx.Rule.Name, - }, - AlertId: evalCtx.Rule.Id, - Level: evalCtx.Rule.Level, - State: string(evalCtx.Rule.State), - EvalData: matches, - AlertRule: newAlertRecordRule(evalCtx), - } - createData := recordCreateInput.JSON(recordCreateInput) - record, err := db.DoCreate(models.AlertRecordManager, evalCtx.Ctx, evalCtx.UserCred, jsonutils.NewDict(), createData, evalCtx.UserCred) - if err != nil { - log.Errorf("create alert record err:%v", err) - } - record.PostCreate(evalCtx.Ctx, evalCtx.UserCred, evalCtx.UserCred, nil, createData) + n.createAlertRecordWhenNotify(evalCtx) } + if !shouldNotify && evalCtx.shouldUpdateAlertState() && evalCtx.NoDataFound { + n.detachAlertResourceWhenNodata(evalCtx) + } + return result, nil } +func (n *notificationService) createAlertRecordWhenNotify(evalCtx *EvalContext) { + var matches []*monitor.EvalMatch + if evalCtx.Firing { + matches = evalCtx.EvalMatches + } else { + matches = evalCtx.AlertOkEvalMatches + } + recordCreateInput := monitor.AlertRecordCreateInput{ + StandaloneResourceCreateInput: apis.StandaloneResourceCreateInput{ + GenerateName: evalCtx.Rule.Name, + }, + AlertId: evalCtx.Rule.Id, + Level: evalCtx.Rule.Level, + State: string(evalCtx.Rule.State), + EvalData: matches, + AlertRule: newAlertRecordRule(evalCtx), + } + createData := recordCreateInput.JSON(recordCreateInput) + record, err := db.DoCreate(models.AlertRecordManager, evalCtx.Ctx, evalCtx.UserCred, jsonutils.NewDict(), createData, evalCtx.UserCred) + if err != nil { + log.Errorf("create alert record err:%v", err) + } + record.PostCreate(evalCtx.Ctx, evalCtx.UserCred, evalCtx.UserCred, nil, createData) +} + +func (n *notificationService) detachAlertResourceWhenNodata(evalCtx *EvalContext) { + errs := models.CommonAlertManager.DetachAlertResourceByAlertId(evalCtx.Ctx, evalCtx.UserCred, evalCtx.Rule.Id) + if len(errs) != 0 { + log.Errorf("detachAlertResourceWhenNodata err:%#v", errors.NewAggregate(errs)) + } +} + type NotifierPlugin struct { Type string Factory NotifierFactory @@ -205,25 +219,14 @@ func InitNotifier(config NotificationConfig) (Notifier, error) { func newAlertRecordRule(evalCtx *EvalContext) monitor.AlertRecordRule { alertRule := monitor.AlertRecordRule{} + if evalCtx.RuleDescription != nil { + alertRule = evalCtx.RuleDescription.AlertRecordRule + } if evalCtx.Rule.Frequency < 60 { alertRule.Period = fmt.Sprintf("%ds", evalCtx.Rule.Frequency) } else { alertRule.Period = fmt.Sprintf("%dm", evalCtx.Rule.Frequency/60) } - ruleStr := evalCtx.Rule.Message - ruleElementArr := strings.Split(ruleStr, " ") - if len(ruleElementArr) == 3 { - alertRule.Metric = ruleElementArr[0] - alertRule.Comparator = ruleElementArr[1] - alertRule.Threshold = ruleElementArr[2] - } - if len(evalCtx.EvalMatches) != 0 { - alertRule.MeasurementDesc = evalCtx.EvalMatches[0].MeasurementDesc - alertRule.FieldDesc = evalCtx.EvalMatches[0].FieldDesc - } - if len(evalCtx.AlertOkEvalMatches) != 0 { - alertRule.MeasurementDesc = evalCtx.AlertOkEvalMatches[0].MeasurementDesc - alertRule.FieldDesc = evalCtx.AlertOkEvalMatches[0].FieldDesc - } + return alertRule } diff --git a/pkg/monitor/models/commonalert.go b/pkg/monitor/models/commonalert.go index 5dba84cff6..d82b12b938 100644 --- a/pkg/monitor/models/commonalert.go +++ b/pkg/monitor/models/commonalert.go @@ -992,15 +992,20 @@ func (alert *SCommonAlert) StartDetachTask(ctx context.Context, userCred mcclien func (alert *SCommonAlert) DetachAlertResourceOnDisable(ctx context.Context, userCred mcclient.TokenCredential) (errs []error) { - resources, err := GetAlertResourceManager().getResourceFromAlertId(alert.Id) + return CommonAlertManager.DetachAlertResourceByAlertId(ctx, userCred, alert.Id) +} + +func (manager *SCommonAlertManager) DetachAlertResourceByAlertId(ctx context.Context, + userCred mcclient.TokenCredential, alertId string) (errs []error) { + resources, err := GetAlertResourceManager().getResourceFromAlertId(alertId) if err != nil { errs = append(errs, errors.Wrap(err, "getResourceFromAlert error")) return } for _, resource := range resources { - err := resource.DetachAlert(ctx, userCred, alert.Id) + err := resource.DetachAlert(ctx, userCred, alertId) if err != nil { - errs = append(errs, errors.Wrapf(err, "resource:%s DetachAlert:%s err", resource.Id, alert.Id)) + errs = append(errs, errors.Wrapf(err, "resource:%s DetachAlert:%s err", resource.Id, alertId)) } } return