// 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 conditions import ( "fmt" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/apis/monitor" "yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/monitor/alerting" "yunion.io/x/onecloud/pkg/monitor/models" "yunion.io/x/onecloud/pkg/monitor/tsdb" ) const ( NO_DATA = "nodata" HOST_TAG_NAME = "name" HOST_TAG_ID = "id" HOST_TAG_IP = "access_ip" RESOURCE_TAG_IP = "ips" HOST_TAG_BRAND = "brand" ) func init() { alerting.RegisterCondition(monitor.METRIC_QUERY_TYPE_NO_DATA, func(model *monitor.AlertCondition, index int) (alerting.Condition, error) { return newNoDataQueryCondition(model, index) }) } type NoDataQueryCondition struct { *QueryCondition } func (c *NoDataQueryCondition) 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 var matches []*monitor.EvalMatch var alertOkmatches []*monitor.EvalMatch normalHostIds := make(map[string]*monitor.EvalMatch, 0) alert, err := models.CommonAlertManager.GetAlert(context.Rule.Id) if err != nil { return nil, errors.Wrap(err, "GetAlert to NewEvalMatch error") } serLoop: for _, series := range seriesList { tagId := monitor.GetMeasurementTagIdKeyByResTypeWithDefault(context.Rule.RuleDescription[0].ResType) for key, val := range series.Tags { if key == tagId { if len(context.Rule.RuleDescription) == 0 { return &alerting.ConditionResult{ Firing: false, NoDataFound: true, Operator: c.Operator, EvalMatches: matches, AlertOkEvalMatches: alertOkmatches, }, nil } reducedValue, valStrArr := c.Reducer.Reduce(series) match, err := c.NewEvalMatch(alert, context, *series, nil, reducedValue, valStrArr, false) if err != nil { return nil, errors.Wrap(err, "NoDataQueryCondition NewEvalMatch error") } normalHostIds[val] = match continue serLoop } } } allResources, err := c.GetQueryResources(auth.GetAdminSession(context.Ctx, ""), "system", true) if err != nil { return nil, errors.Wrap(err, "GetQueryResources err") } for _, host := range allResources { id, _ := host.GetString("id") evalMatch, err := c.NewNoDataEvalMatch(context, alert, host) if err != nil { return nil, errors.Wrapf(err, "NewNoDataEvalMatch of host: %s", host.String()) } if normalMatch, ok := normalHostIds[id]; !ok { c.createEvalMatchTagFromHostJson(context, evalMatch, host) matches = append(matches, evalMatch) } else { c.createEvalMatchTagFromHostJson(context, normalMatch, host) if err := OkEvalMatchSetIsRecovery(alert, id, normalMatch); err != nil { log.Warningf("[NoDataQuery] set eval match %s to recovered: %v", jsonutils.Marshal(normalMatch), err) } alertOkmatches = append(alertOkmatches, normalMatch) } } return &alerting.ConditionResult{ Firing: len(matches) > 0, NoDataFound: 0 == len(seriesList), Operator: c.Operator, EvalMatches: matches, AlertOkEvalMatches: alertOkmatches, }, nil } /*func (c *NoDataQueryCondition) convertTagsQuery(evalContext *alerting.EvalContext, query *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { alertDetails, err := c.GetCommonAlertDetails(evalContext) if err != nil { return nil, err } for i, _ := range c.Query.Model.Tags { filterCount := 0 if c.Query.Model.Tags[i].Operator == "=" { if tag, ok := monitor.MEASUREMENT_TAG_KEYWORD[alertDetails.ResType]; ok { if c.Query.Model.Tags[i].Key == tag { query.Set("name", jsonutils.NewString(c.Query.Model.Tags[i].Value)) continue } } } query.Set(fmt.Sprintf("filter.%d", filterCount), jsonutils.NewString(fmt.Sprintf("%s.notin(%s)", c.Query.Model.Tags[i].Key, c.Query.Model.Tags[i].Value))) filterCount++ } return query, nil }*/ func (c *NoDataQueryCondition) NewNoDataEvalMatch(context *alerting.EvalContext, alert *models.SCommonAlert, host jsonutils.JSONObject) (*monitor.EvalMatch, error) { evalMatch := new(monitor.EvalMatch) settings, _ := alert.GetSettings() alertDetails := alert.GetCommonAlertMetricDetailsFromAlertCondition(c.Index, &settings.Conditions[c.Index]) evalMatch.Metric = fmt.Sprintf("%s.%s", alertDetails.Measurement, alertDetails.Field) queryKeyInfo := "" if len(alertDetails.MeasurementDisplayName) > 0 && len(alertDetails.FieldDescription.DisplayName) > 0 { queryKeyInfo = fmt.Sprintf("%s.%s", alertDetails.MeasurementDisplayName, alertDetails.FieldDescription.DisplayName) } if len(queryKeyInfo) == 0 { queryKeyInfo = evalMatch.Metric } evalMatch.Unit = alertDetails.FieldDescription.Unit msg := fmt.Sprintf("%s.%s %s ", alertDetails.Measurement, alertDetails.Field, alertDetails.Comparator) context.Rule.Message = msg //evalMatch.Condition = c.GenerateFormatCond(meta, queryKeyInfo).String() evalMatch.ValueStr = NO_DATA evalMatch.AlertDetails = jsonutils.Marshal(alertDetails) return evalMatch, nil } func (c *NoDataQueryCondition) createEvalMatchTagFromHostJson( evalContext *alerting.EvalContext, evalMatch *monitor.EvalMatch, host jsonutils.JSONObject, ) { evalMatch.Tags = make(map[string]string, 0) ip, _ := host.GetString(HOST_TAG_IP) if ip == "" { ip, _ = host.GetString(RESOURCE_TAG_IP) } name, _ := host.GetString(HOST_TAG_NAME) id, _ := host.GetString("id") brand, _ := host.GetString(HOST_TAG_BRAND) evalMatch.Tags["ip"] = ip evalMatch.Tags[HOST_TAG_NAME] = name evalMatch.Tags[HOST_TAG_BRAND] = brand switch evalContext.Rule.RuleDescription[0].ResType { case monitor.METRIC_RES_TYPE_GUEST: case monitor.METRIC_RES_TYPE_RDS: case monitor.METRIC_RES_TYPE_REDIS: case monitor.METRIC_RES_TYPE_OSS: default: evalMatch.Tags["host"] = name evalMatch.Tags[monitor.GetMeasurementTagIdKeyByResType(monitor.METRIC_RES_TYPE_HOST)] = id evalMatch.Tags[hostconsts.TELEGRAF_TAG_KEY_RES_TYPE] = hostconsts.TELEGRAF_TAG_ONECLOUD_RES_TYPE evalMatch.Tags[hostconsts.TELEGRAF_TAG_KEY_HOST_TYPE] = hostconsts.TELEGRAF_TAG_ONECLOUD_HOST_TYPE_HOST } } func newNoDataQueryCondition(model *monitor.AlertCondition, index int) (*NoDataQueryCondition, error) { queryCondition, err := newQueryCondition(model, index) if err != nil { return nil, err } condition := new(NoDataQueryCondition) condition.QueryCondition = queryCondition return condition, nil }