diff --git a/pkg/apis/monitor/suggestsysalert.go b/pkg/apis/monitor/suggestsysalert.go index b67b978831..1a0d933bd6 100644 --- a/pkg/apis/monitor/suggestsysalert.go +++ b/pkg/apis/monitor/suggestsysalert.go @@ -50,13 +50,14 @@ type SuggestSysAlertCreateInput struct { type SuggestSysAlertDetails struct { apis.VirtualResourceDetails compute.CloudregionResourceInfo - RuleName string `json:"rule_name"` - ShowName string `json:"show_name"` - ResType string `json:"res_type"` - Suggest string `json:"suggest"` - Brand string `json:"brand"` - Account string `json:"account"` - ResName string `json:"res_name"` + RuleName string `json:"rule_name"` + ShowName string `json:"show_name"` + ResType string `json:"res_type"` + Suggest string `json:"suggest"` + Brand string `json:"brand"` + Account string `json:"account"` + ResName string `json:"res_name"` + CommonAlertMetricDetails []*CommonAlertMetricDetails `json:"common_alert_metric_details"` } type SuggestSysAlertUpdateInput struct { diff --git a/pkg/apis/monitor/suggestsysrule.go b/pkg/apis/monitor/suggestsysrule.go index cf5dd169e0..79e264b466 100644 --- a/pkg/apis/monitor/suggestsysrule.go +++ b/pkg/apis/monitor/suggestsysrule.go @@ -23,6 +23,11 @@ import ( const ( METRIC_TAG = "TAG" METRIC_FIELD = "FIELD" + + METRIC_VM_ID = "vm_id" + METRIC_OSS_ID = "oss_id" + METRIC_RDS_ID = "rds_id" + METRIC_REDIS_ID = "redis_id" ) var PROPERTY_TYPE = []string{"databases", "measurements", "metric-measurement"} diff --git a/pkg/cloudcommon/db/opslog_const.go b/pkg/cloudcommon/db/opslog_const.go index bf580fc4f5..dc5b53f79f 100644 --- a/pkg/cloudcommon/db/opslog_const.go +++ b/pkg/cloudcommon/db/opslog_const.go @@ -274,4 +274,6 @@ const ( ACT_NETWORK_REMOVE_VPC_FAILED = "network_remove_vpc_failed" ACT_NETWORK_MODIFY_ROUTE = "network_modify_route" ACT_NETWORK_MODIFY_ROUTE_FAILED = "network_modify_route_failed" + + ACT_UPDATE_RULE = "update_config" ) diff --git a/pkg/monitor/alerting/conditions/suggestquery.go b/pkg/monitor/alerting/conditions/suggestquery.go new file mode 100644 index 0000000000..14b86fabd9 --- /dev/null +++ b/pkg/monitor/alerting/conditions/suggestquery.go @@ -0,0 +1,88 @@ +package conditions + +import ( + "fmt" + + "yunion.io/x/onecloud/pkg/apis/monitor" + "yunion.io/x/onecloud/pkg/monitor/alerting" + "yunion.io/x/onecloud/pkg/monitor/tsdb" +) + +func init() { + alerting.RegisterCondition("suggest_query", func(model *monitor.AlertCondition, index int) (alerting.Condition, + error) { + return newSuggestQueryCondition(model, index) + }) +} + +type SuggestQueryCondition struct { + *QueryCondition +} + +func newSuggestQueryCondition(model *monitor.AlertCondition, index int) (*SuggestQueryCondition, error) { + queryCondition, err := newQueryCondition(model, index) + if err != nil { + return nil, err + } + condition := new(SuggestQueryCondition) + condition.QueryCondition = queryCondition + return condition, nil +} + +func (c *SuggestQueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { + timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) + ret, err := c.executeQuery(context, timeRange) + if err != nil { + return nil, err + } + seriesList := ret.series + emptySeriesCount := 0 + evalMatchCount := 0 + var matches []*monitor.EvalMatch + for _, series := range seriesList { + reducedValue, _ := c.Reducer.Reduce(series) + evalMatch := c.Evaluator.Eval(reducedValue) + if reducedValue == nil { + emptySeriesCount++ + } + if context.IsTestRun { + context.Logs = append(context.Logs, &monitor.ResultLogEntry{ + Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %v", c.Index, evalMatch, series.Name, reducedValue), + }) + } + if evalMatch { + evalMatchCount++ + } + if evalMatch { + matches = append(matches, &monitor.EvalMatch{ + Metric: series.Name, + Value: reducedValue, + Tags: series.Tags, + }) + } + } + + // handle no series special case + if len(seriesList) == 0 { + // eval condition for null value + evalMatch := c.Evaluator.Eval(nil) + if context.IsTestRun { + context.Logs = append(context.Logs, &monitor.ResultLogEntry{ + Message: fmt.Sprintf("Condition: Eval: %v, Query returned No Series (reduced to null/no value)", evalMatch), + }) + } + if evalMatch { + evalMatchCount++ + matches = append(matches, &monitor.EvalMatch{ + Metric: "NoData", + Value: nil, + }) + } + } + return &alerting.ConditionResult{ + Firing: evalMatchCount > 0, + NoDataFound: emptySeriesCount == len(seriesList), + Operator: c.Operator, + EvalMatches: matches, + }, nil +} diff --git a/pkg/monitor/models/commonalert.go b/pkg/monitor/models/commonalert.go index 61053fcd0d..ad6dbd6c5d 100644 --- a/pkg/monitor/models/commonalert.go +++ b/pkg/monitor/models/commonalert.go @@ -954,7 +954,6 @@ func (alert *SCommonAlert) PerformConfig(ctx context.Context, userCred mcclient. period, _ := data.GetString("period") comparator, _ := data.GetString("comparator") threshold, _ := data.GetString("threshold") - fmt.Println(threshold) if len(period) != 0 { if _, err := time.ParseDuration(period); err != nil { return data, httperrors.NewInputParameterError("Invalid period format: %s", period) @@ -989,6 +988,7 @@ func (alert *SCommonAlert) PerformConfig(ctx context.Context, userCred mcclient. alert.Settings = jsonutils.Marshal(setting) return nil }) + PerformConfigLog(alert, userCred) return jsonutils.Marshal(alert), err } diff --git a/pkg/monitor/models/suggestsysrule.go b/pkg/monitor/models/suggestsysrule.go index d352645538..f3d402a81d 100644 --- a/pkg/monitor/models/suggestsysrule.go +++ b/pkg/monitor/models/suggestsysrule.go @@ -25,7 +25,6 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" - "yunion.io/x/pkg/tristate" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis/monitor" @@ -37,6 +36,7 @@ import ( merrors "yunion.io/x/onecloud/pkg/monitor/errors" "yunion.io/x/onecloud/pkg/monitor/registry" "yunion.io/x/onecloud/pkg/util/influxdb" + "yunion.io/x/onecloud/pkg/util/logclient" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -328,14 +328,11 @@ func (self *SSuggestSysRule) AllowPerformEnable(ctx context.Context, userCred mc } func (self *SSuggestSysRule) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if !self.Enabled.Bool() { - db.Update(self, func() error { - self.Enabled = tristate.True - return nil - }) - db.OpsLog.LogEvent(self, db.ACT_ENABLE, "", userCred) - self.updateCronjob() + err := db.EnabledPerformEnable(self, ctx, userCred, true) + if err != nil { + return nil, errors.Wrap(err, "EnabledPerformEnable") } + self.updateCronjob() return nil, nil } @@ -344,14 +341,11 @@ func (self *SSuggestSysRule) AllowPerformDisable(ctx context.Context, userCred m } func (self *SSuggestSysRule) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if self.Enabled.IsTrue() { - db.Update(self, func() error { - self.Enabled = tristate.False - return nil - }) - db.OpsLog.LogEvent(self, db.ACT_DISABLE, "", userCred) - self.updateCronjob() + err := db.EnabledPerformEnable(self, ctx, userCred, false) + if err != nil { + return nil, errors.Wrap(err, "EnabledPerformEnable") } + self.updateCronjob() return nil, nil } @@ -386,11 +380,16 @@ func (self *SSuggestSysRule) PerformConfig(ctx context.Context, userCred mcclien } return nil }) - db.OpsLog.LogEvent(self, "modifyconfig", "", userCred) + PerformConfigLog(self, userCred) self.updateCronjob() return nil, nil } +func PerformConfigLog(model db.IModel, userCred mcclient.TokenCredential) { + db.OpsLog.LogEvent(model, db.ACT_UPDATE_RULE, "", userCred) + logclient.AddSimpleActionLog(model, logclient.ACT_UPDATE_RULE, nil, userCred, true) +} + func (self *SSuggestSysRuleManager) AllowGetPropertyRuleType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { return true } diff --git a/pkg/monitor/suggestsysdrivers/influxdbbasedriver.go b/pkg/monitor/suggestsysdrivers/influxdbbasedriver.go index 5c6d8ff634..a6e1f46d61 100644 --- a/pkg/monitor/suggestsysdrivers/influxdbbasedriver.go +++ b/pkg/monitor/suggestsysdrivers/influxdbbasedriver.go @@ -91,6 +91,7 @@ func (drv *InfluxdbBaseDriver) GetLatestAlerts(rule *models.SSuggestSysRule, ins return ret, errors.Wrap(err, "rule getScaleEvalResult happen error") } if firing { + log.Errorf("evalMatchMapLen:%d", len(evalMatchMap)) serverArr, err := drv.getResourcesByEvalMatchsMap(evalMatchMap, instance) if err != nil { return ret, errors.Wrap(err, "rule getResource error") @@ -106,7 +107,7 @@ func (drv *InfluxdbBaseDriver) getScaleEvalResult(scales []monitor.Scale) (bool, scaleEvalMatchs := make(map[string][]*monitor.EvalMatch, 0) for index, scale := range scales { condition := monitor.AlertCondition{ - Type: "query", + Type: "suggest_query", Query: drv.newAlertQuery(scale), Evaluator: monitor.Condition{Type: getQueryEvalType(scale), Params: []float64{scale.Threshold}}, Reducer: monitor.Condition{Type: "avg"}, @@ -119,7 +120,7 @@ func (drv *InfluxdbBaseDriver) getScaleEvalResult(scales []monitor.Scale) (bool, jsonutils.Marshal(condition)) } duration, _ := time.ParseDuration(condition.Query.From) - queryCon := queryCondition.(*conditions.QueryCondition) + queryCon := queryCondition.(*conditions.SuggestQueryCondition) queryCon.Reducer = conditions.NewSuggestRuleReducer(queryCon.Reducer.GetType(), duration) //evalContext := alerting.NewEvalContext(context.Background(), auth.AdminCredential(), nil) evalContext := alerting.EvalContext{ @@ -175,8 +176,9 @@ func (drv *InfluxdbBaseDriver) getResourcesByEvalMatchsMap(evalMatchsMap map[str } resArr := jsonutils.NewArray() for _, evalMatch := range maxEvalMatch { - res, mappingId, mappingVal := drv.getResourceFromEvalMatch(evalMatch) - if mappingId == "" { + res, mappingId, err := drv.getResourceFromEvalMatch(evalMatch) + if err != nil { + log.Errorln(err) continue } suggestSysAlert, err := getSuggestSysAlertFromJson(res, drv) @@ -185,28 +187,44 @@ func (drv *InfluxdbBaseDriver) getResourcesByEvalMatchsMap(evalMatchsMap map[str } suggestSysAlert.Action = string(monitor.SCALE_DOWN_DRIVER_ACTION) suggestSysAlert.MonitorConfig = jsonutils.Marshal(instance) - suggestSysAlert.Problem = describeEvalResultTojson(evalMatchsMap, mappingId, mappingVal) + suggestSysAlert.Problem = drv.describeEvalResultTojson(evalMatchsMap, mappingId) resArr.Add(jsonutils.Marshal(suggestSysAlert)) } return resArr.GetArray() } -func (drv *InfluxdbBaseDriver) getResourceFromEvalMatch(evalMatch *monitor.EvalMatch) (jsonutils.JSONObject, string, string) { - idTag := getMetricIdTag(evalMatch.Tags) +func (drv *InfluxdbBaseDriver) getResourceFromEvalMatch(evalMatch *monitor.EvalMatch) (jsonutils.JSONObject, string, error) { var server jsonutils.JSONObject mappingId := "" - mappingVal := "" - for id, val := range idTag { - serverobj, err := drv.getResourceById(id) - if err != nil { - continue - } - server = serverobj - mappingId = id - mappingVal = val - break + id, err := drv.getMetricId(evalMatch) + if err != nil { + return server, mappingId, errors.Wrap(err, "InfluxdbBaseDriver getMetricId err") } - return server, mappingId, mappingVal + serverobj, err := drv.getResourceById(id) + if err != nil { + return server, mappingId, errors.Wrapf(err, "InfluxdbBaseDriver getResourceById:%s err", id) + } + server = serverobj + mappingId = id + return server, mappingId, nil +} + +func (drv *InfluxdbBaseDriver) getMetricId(evalMatch *monitor.EvalMatch) (string, error) { + var id string + switch drv.GetResourceType() { + case monitor.SCALE_MONTITOR_RES_TYPE: + id = evalMatch.Tags[monitor.METRIC_VM_ID] + case monitor.REDIS_UNREASONABLE_MONITOR_RES_TYPE: + id = evalMatch.Tags[monitor.METRIC_REDIS_ID] + case monitor.RDS_UNREASONABLE_MONITOR_RES_TYPE: + id = evalMatch.Tags[monitor.METRIC_RDS_ID] + case monitor.OSS_UNREASONABLE_MONITOR_RES_TYPE: + id = evalMatch.Tags[monitor.METRIC_OSS_ID] + } + if len(id) == 0 { + return id, fmt.Errorf("no find resourceId by the driver type:%s", string(drv.GetResourceType())) + } + return id, nil } func (drv *InfluxdbBaseDriver) getResourceById(id string) (jsonutils.JSONObject, error) { @@ -223,15 +241,16 @@ func (drv *InfluxdbBaseDriver) getResourceById(id string) (jsonutils.JSONObject, return nil, fmt.Errorf("unsupporttd to get resource by the driver type:%s", string(drv.GetResourceType())) } -func describeEvalResultTojson(evalMatchsMap map[string][]*monitor.EvalMatch, mappingId, mappingVal string) jsonutils.JSONObject { +func (drv *InfluxdbBaseDriver) describeEvalResultTojson(evalMatchsMap map[string][]*monitor.EvalMatch, + mappingId string) jsonutils.JSONObject { problem := jsonutils.NewDict() +loopEvalMap: for _, evalMatchs := range evalMatchsMap { for _, evalMatch := range evalMatchs { - idTag := getMetricIdTag(evalMatch.Tags) - if val, ok := idTag[mappingId]; ok { - if val == mappingVal { - problem.Add(jsonutils.NewFloat64(*evalMatch.Value), evalMatch.Metric) - } + metricId, _ := drv.getMetricId(evalMatch) + if metricId == mappingId { + problem.Add(jsonutils.NewFloat64(*evalMatch.Value), evalMatch.Metric) + break loopEvalMap } } } diff --git a/pkg/util/logclient/consts.go b/pkg/util/logclient/consts.go index 52606c62e7..8836ea80c4 100644 --- a/pkg/util/logclient/consts.go +++ b/pkg/util/logclient/consts.go @@ -192,4 +192,6 @@ const ( ACT_NETWORK_ADD_VPC = "network_add_vpc" ACT_NETWORK_REMOVE_VPC = "network_remove_vpc" ACT_NETWORK_MODIFY_ROUTE = "network_modify_route" + + ACT_UPDATE_RULE = "update_config" ) diff --git a/pkg/util/logclient/consts_i18n.go b/pkg/util/logclient/consts_i18n.go index 8eff64300a..7da19d7492 100644 --- a/pkg/util/logclient/consts_i18n.go +++ b/pkg/util/logclient/consts_i18n.go @@ -653,4 +653,8 @@ func init() { EN("Modify Network Route"). CN("修改网络路由策略"), ) + t.Set(ACT_UPDATE_RULE, i18n.NewTableEntry(). + EN("Update RuleConfig"). + CN("调整规则配置"), + ) }