Files
cloudpods/pkg/monitor/alerting/eval_context.go
zhaoxiangchun 0a7c587117 feat(monitor): monitor alertRecord support set shield
1.报警记录支持设置屏蔽时间
2.报警记录展示重构
3.报警通知中过滤已经设置屏蔽的资源
2021-07-13 20:14:39 +08:00

258 lines
6.8 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package alerting
import (
"context"
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/apis/monitor"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
// EvalContext is the context object for an alert evaluation.
type EvalContext struct {
Firing bool
IsTestRun bool
IsDebug bool
EvalMatches []*monitor.EvalMatch
AlertOkEvalMatches []*monitor.EvalMatch
Logs []*monitor.ResultLogEntry
Error error
ConditionEvals string
StartTime time.Time
EndTime time.Time
Rule *Rule
//RuleDescription *RuleDescription
NoDataFound bool
PrevAlertState monitor.AlertStateType
Ctx context.Context
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{
Ctx: alertCtx,
UserCred: userCred,
StartTime: time.Now(),
Rule: rule,
EvalMatches: make([]*monitor.EvalMatch, 0),
PrevAlertState: rule.State,
}
}
// SateDescription contains visual information about the alert state.
type StateDescription struct {
//Color string
Text string
Data string
}
// GetStateModel returns the `StateDescription` based on current state.
func (c *EvalContext) GetStateModel() *StateDescription {
switch c.Rule.State {
case monitor.AlertStateOK:
return &StateDescription{
Text: "OK",
}
case monitor.AlertStateNoData:
return &StateDescription{
Text: "No Data",
}
case monitor.AlertStateAlerting:
return &StateDescription{
Text: "Alerting",
}
case monitor.AlertStateUnknown:
return &StateDescription{
Text: "Unknown",
}
default:
panic(fmt.Sprintf("Unknown rule state %q for alert %s", c.Rule.State, c.Rule.Name))
}
}
func (c *EvalContext) shouldUpdateAlertState() bool {
return c.Rule.State != c.PrevAlertState || c.Rule.State == monitor.AlertStateAlerting
}
// GetDurationMs returns the duration of the alert evaluation.
func (c *EvalContext) GetDurationMs() float64 {
return float64(c.EndTime.Nanosecond()-c.StartTime.Nanosecond()) / float64(1000000)
}
func (c *EvalContext) GetRuleTitle() string {
rule := c.Rule
if rule.Title != "" {
return rule.Title
}
return rule.Name
}
// GetNotificationTitle returns the title of the alert rule including alert state.
func (c *EvalContext) GetNotificationTitle() string {
return "[" + c.GetStateModel().Text + "] " + c.GetRuleTitle()
}
func (c *EvalContext) GetCallbackURLPrefix() string {
config, err := modules.ServicesV3.GetSpecific(auth.GetAdminSession(c.Ctx, "", ""), "common", "config",
jsonutils.NewDict())
if err != nil {
log.Errorf("GetCallbackURLPrefix err:%v", err)
return ""
}
url, _ := config.GetString("config", "default", "api_server")
defaultWebUri := "alertrecord"
matchTag := map[string]string{}
if c.Firing {
matchTag = c.EvalMatches[0].Tags
} else {
matchTag = c.AlertOkEvalMatches[0].Tags
}
if uri, ok := matchTag["web_url"]; ok {
defaultWebUri = uri
}
return fmt.Sprintf("%s/%s", url, defaultWebUri)
}
// GetNewState returns the new state from the alert rule evaluation.
func (c *EvalContext) GetNewState() monitor.AlertStateType {
ns := getNewStateInternal(c)
if ns != monitor.AlertStateAlerting || c.Rule.For == 0 {
return ns
}
since := time.Since(c.Rule.LastStateChange)
if c.PrevAlertState == monitor.AlertStatePending && since/time.Second >= c.Rule.For {
return monitor.AlertStateAlerting
}
if c.Rule.For != 0 {
log.Errorf("ruleName:%s,since:%d,for:%d", c.Rule.Name, since/time.Second, c.Rule.For)
}
if ns == monitor.AlertStateAlerting && since/time.Second >= c.Rule.For {
return monitor.AlertStateAlerting
}
return monitor.AlertStatePending
}
func getNewStateInternal(c *EvalContext) monitor.AlertStateType {
if c.Error != nil {
log.Errorf("Alert Rule Result Error, ruleId: %s, name: %s, error: %v, changing state to %v",
c.Rule.Id,
c.Rule.Name,
c.Error,
c.Rule.ExecutionErrorState.ToAlertState())
if c.Rule.ExecutionErrorState == monitor.ExecutionErrorKeepState {
return c.PrevAlertState
}
return c.Rule.ExecutionErrorState.ToAlertState()
}
if c.Firing {
return monitor.AlertStateAlerting
}
if c.NoDataFound {
log.Infof("Alert Rule returned no data, ruleId: %s, name: %s, changing state to %v",
c.Rule.Id,
c.Rule.Name,
c.Rule.NoDataState.ToAlertState())
if c.Rule.NoDataState == monitor.NoDataKeepState {
return c.PrevAlertState
}
return c.Rule.NoDataState.ToAlertState()
}
return monitor.AlertStateOK
}
func (c *EvalContext) GetNotificationTemplateConfig() monitor.NotificationTemplateConfig {
desc := c.Rule.Message
if c.Error != nil {
if desc != "" {
desc += "\n"
}
desc += "Error: " + c.Error.Error()
}
return monitor.NotificationTemplateConfig{
Title: c.GetNotificationTitle(),
Name: c.Rule.Name,
ResourceName: c.GetResourceNameOfMathes(nil),
Matches: c.GetEvalMatches(),
StartTime: c.StartTime.Format("2006-01-02 15:04:05"),
EndTime: c.EndTime.Format("2006-01-02 15:04:05"),
Description: desc,
Level: c.Rule.Level,
NoDataFound: c.NoDataFound,
WebUrl: c.GetCallbackURLPrefix(),
}
}
func (c *EvalContext) GetEvalMatches() []monitor.EvalMatch {
ret := make([]monitor.EvalMatch, 0)
matches := c.EvalMatches
if !c.Firing {
matches = c.AlertOkEvalMatches
}
for _, c := range matches {
if _, ok := c.Tags[monitor.ALERT_RESOURCE_RECORD_SHIELD_KEY]; ok {
continue
}
ret = append(ret, monitor.EvalMatch{
Condition: c.Condition,
Value: c.Value,
ValueStr: c.ValueStr,
Metric: c.Metric,
Tags: c.Tags,
})
}
return ret
}
func (c *EvalContext) GetResourceNameOfMathes(matches []monitor.EvalMatch) string {
names := strings.Builder{}
if matches == nil {
matches = c.GetEvalMatches()
}
for i, match := range matches {
if name, ok := match.Tags["name"]; ok {
names.WriteString(name)
names.WriteString(fmt.Sprintf("(%s)", match.ValueStr))
if i < len(matches)-1 {
names.WriteString("、")
}
}
}
return names.String()
}