diff --git a/pkg/apis/monitor/commalert.go b/pkg/apis/monitor/commalert.go index 0757d65754..75a400bc1d 100644 --- a/pkg/apis/monitor/commalert.go +++ b/pkg/apis/monitor/commalert.go @@ -117,7 +117,9 @@ type CommonAlertDetails struct { NotifierId string `json:"notifier_id"` Channel []string `json:"channel"` Recipients []string `json:"recipients"` - Status string `json:"status"` + // 静默期 + SilentPeriod string `json:"silent_period"` + Status string `json:"status"` // 报警类型 AlertType string `json:"alert_type"` CommonAlertMetricDetails []*CommonAlertMetricDetails `json:"common_alert_metric_details"` diff --git a/pkg/monitor/alerting/conditions/nodataquery.go b/pkg/monitor/alerting/conditions/nodataquery.go index 96c0996c64..9108ecc0f3 100644 --- a/pkg/monitor/alerting/conditions/nodataquery.go +++ b/pkg/monitor/alerting/conditions/nodataquery.go @@ -3,6 +3,7 @@ package conditions import ( "context" "fmt" + "sort" "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" @@ -20,9 +21,10 @@ import ( const ( NO_DATA = "nodata" - HOST_TAG_NAME = "name" - HOST_TAG_IP = "access_ip" - HOST_TAG_BRAND = "brand" + HOST_TAG_NAME = "name" + HOST_TAG_IP = "access_ip" + RESOURCE_TAG_IP = "ips" + HOST_TAG_BRAND = "brand" ) func init() { @@ -76,6 +78,7 @@ serLoop: } } allHosts, err := c.getOnecloudResources(context) + allHosts = c.filterAllResources(context, allHosts) if err != nil { return nil, errors.Wrap(err, "NoDataQueryCondition getOnecloudHosts error") } @@ -102,6 +105,128 @@ serLoop: }, nil } +func (c *NoDataQueryCondition) filterAllResources(context *alerting.EvalContext, + resources []jsonutils.JSONObject) []jsonutils.JSONObject { + if len(c.Query.Model.Tags) == 0 { + return resources + } + filterIdMap := make(map[string]jsonutils.JSONObject) + filterQuery := c.getFilterQuery() + intKey := make([]int, 0) + if len(filterQuery) != 0 { + for key, _ := range filterQuery { + intKey = append(intKey, key) + } + sort.Ints(intKey) + minKey := intKey[0] + if minKey != 0 { + filterQuery[0] = minKey - 1 + } + } else { + filterQuery[0] = len(c.Query.Model.Tags) - 1 + } + for start, end := range filterQuery { + filterResources := c.getFilterResources(context, start, end, resources) + filterIdMap = c.fillFilterRes(filterResources, filterIdMap) + } + filterRes := make([]jsonutils.JSONObject, 0) + for _, obj := range filterIdMap { + filterRes = append(filterRes, obj) + } + return filterRes +} + +func (c *NoDataQueryCondition) fillFilterRes(filterRes []jsonutils.JSONObject, + filterIdMap map[string]jsonutils.JSONObject) map[string]jsonutils.JSONObject { + for _, res := range filterRes { + id, _ := res.GetString("id") + if _, ok := filterIdMap[id]; !ok { + filterIdMap[id] = res + } + } + return filterIdMap +} + +func (c *NoDataQueryCondition) getFilterQuery() map[int]int { + length := len(c.Query.Model.Tags) + tagIndexMap := make(map[int]int) + for i := 0; i < length; i++ { + if c.Query.Model.Tags[i].Condition == "OR" { + andIndex := c.getTheAndOfConditionor(i + 1) + if andIndex == i+1 { + tagIndexMap[i] = i + continue + } + if andIndex == length { + for j := i; j < length; j++ { + tagIndexMap[j] = j + } + break + } + tagIndexMap[i] = andIndex + i = andIndex + } + } + return tagIndexMap +} + +func (c *NoDataQueryCondition) getTheAndOfConditionor(start int) int { + for i := start; i < len(c.Query.Model.Tags); i++ { + if c.Query.Model.Tags[i].Condition != "AND" { + return i + } + } + return len(c.Query.Model.Tags) +} + +func (c *NoDataQueryCondition) getFilterResources(evalContext *alerting.EvalContext, start int, end int, + resources []jsonutils.JSONObject) []jsonutils.JSONObject { + relationMap := c.getTagKeyRelationMap(evalContext) + tmp := resources + for i := start; i <= end; i++ { + tag := c.Query.Model.Tags[i] + relationKey := relationMap[tag.Key] + filterObj := make([]jsonutils.JSONObject, 0) + for _, res := range tmp { + val, _ := res.GetString(relationKey) + if c.Query.Model.Tags[i].Operator == "=" { + if val == c.Query.Model.Tags[i].Value { + filterObj = append(filterObj, res) + } + } + if c.Query.Model.Tags[i].Operator == "!=" { + if val != c.Query.Model.Tags[i].Value { + filterObj = append(filterObj, res) + } + } + } + tmp = filterObj + if len(tmp) == 0 { + return tmp + } + } + return tmp +} + +func (c *NoDataQueryCondition) getTagKeyRelationMap(evalContext *alerting.EvalContext) map[string]string { + relationMap := make(map[string]string) + switch evalContext.Rule.RuleDescription[0].ResType { + case monitor.METRIC_RES_TYPE_HOST: + relationMap = HostTags + case monitor.METRIC_RES_TYPE_GUEST: + relationMap = ServerTags + case monitor.METRIC_RES_TYPE_RDS: + relationMap = RdsTags + case monitor.METRIC_RES_TYPE_REDIS: + relationMap = RedisTags + case monitor.METRIC_RES_TYPE_OSS: + relationMap = OssTags + default: + relationMap = HostTags + } + return relationMap +} + func (c *NoDataQueryCondition) getOnecloudResources(evalContext *alerting.EvalContext) ([]jsonutils.JSONObject, error) { var err error allResources := make([]jsonutils.JSONObject, 0) @@ -111,12 +236,12 @@ func (c *NoDataQueryCondition) getOnecloudResources(evalContext *alerting.EvalCo query := jsonutils.NewDict() query.Add(jsonutils.NewStringArray([]string{"running", "ready"}), "status") query.Add(jsonutils.NewString("true"), "admin") - if len(c.Query.Model.Tags) != 0 { - query, err = c.convertTagsQuery(evalContext, query) - if err != nil { - return nil, errors.Wrap(err, "NoDataQueryCondition convertTagsQuery error") - } - } + //if len(c.Query.Model.Tags) != 0 { + // query, err = c.convertTagsQuery(evalContext, query) + // if err != nil { + // return nil, errors.Wrap(err, "NoDataQueryCondition convertTagsQuery error") + // } + //} switch evalContext.Rule.RuleDescription[0].ResType { case monitor.METRIC_RES_TYPE_HOST: query.Set("host-type", jsonutils.NewString(hostconsts.TELEGRAF_TAG_KEY_HYPERVISOR)) @@ -171,6 +296,7 @@ func ListAllResources(manager modulebase.Manager, params *jsonutils.JSONDict) ([ } params.Add(jsonutils.NewString("system"), "scope") params.Add(jsonutils.NewInt(0), "limit") + params.Add(jsonutils.NewBool(true), "details") var count int session := auth.GetAdminSession(context.Background(), "", "") objs := make([]jsonutils.JSONObject, 0) @@ -223,6 +349,9 @@ func (c *NoDataQueryCondition) createEvalMatchTagFromHostJson(evalContext *alert 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) brand, _ := host.GetString(HOST_TAG_BRAND) evalMatch.Tags["ip"] = ip @@ -250,3 +379,138 @@ func newNoDataQueryCondition(model *monitor.AlertCondition, index int) (*NoDataQ condition.QueryCondition = queryCondition return condition, nil } + +var ( + ServerTags = map[string]string{ + "host": "host", + "host_id": "host_id", + "vm_id": "id", + "vm_ip": "ips", + "vm_name": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "region_ext_id": "region_ext_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "scaling_group_id": "vm_scaling_group_id", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + HostTags = map[string]string{ + "host_id": "id", + "host_ip": "ips", + "host": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "region_ext_id": "region_ext_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + RdsTags = map[string]string{ + "host": "host", + "host_id": "host_id", + "rds_id": "id", + "rds_ip": "ips", + "rds_name": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "region_ext_id": "region_ext_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + RedisTags = map[string]string{ + "host": "host", + "host_id": "host_id", + "redis_id": "id", + "redis_ip": "ips", + "redis_name": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "region_ext_id": "region_ext_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + OssTags = map[string]string{ + "host": "host", + "host_id": "host_id", + "oss_id": "id", + "oss_ip": "ips", + "oss_name": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "region_ext_id": "region_ext_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + ElbTags = map[string]string{ + "host": "host", + "host_id": "host_id", + "elb_id": "id", + "elb_ip": "ips", + "elb_name": "name", + "zone": "zone", + "zone_id": "zone_id", + "zone_ext_id": "zone_ext_id", + "os_type": "os_type", + "status": "status", + "region": "region", + "cloudregion": "cloudregion", + "cloudregion_id": "cloudregion_id", + "tenant": "tenant", + "tenant_id": "tenant_id", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } + + CloudAccountTags = map[string]string{ + "cloudaccount_id": "id", + "cloudaccount_name": "name", + "brand": "brand", + "domain_id": "domain_id", + "project_domain": "project_domain", + } +) diff --git a/pkg/monitor/alerting/conditions/query.go b/pkg/monitor/alerting/conditions/query.go index b778653d7f..89f802931c 100644 --- a/pkg/monitor/alerting/conditions/query.go +++ b/pkg/monitor/alerting/conditions/query.go @@ -233,12 +233,6 @@ func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb queryKeyInfo = evalMatch.Metric } evalMatch.Unit = alertDetails.FieldDescription.Unit - msg := fmt.Sprintf("%s.%s %s %s", alertDetails.Measurement, alertDetails.Field, - alertDetails.Comparator, alerting.RationalizeValueFromUnit(alertDetails.Threshold, evalMatch.Unit, "")) - if len(context.Rule.Message) == 0 { - context.Rule.Message = msg - } - //evalMatch.Condition = c.GenerateFormatCond(meta, queryKeyInfo).String() evalMatch.Tags = c.filterTags(series.Tags, *alertDetails) evalMatch.Value = value evalMatch.ValueStr = alerting.RationalizeValueFromUnit(*value, alertDetails.FieldDescription.Unit, @@ -246,7 +240,14 @@ func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb if alertDetails.GetPointStr { evalMatch.ValueStr = c.jointPointStr(series, evalMatch.ValueStr, valStrArr) } + c.FetchCustomizeEvalMatch(context, evalMatch, alertDetails) //c.newRuleDescription(context, alertDetails) + //evalMatch.Condition = c.GenerateFormatCond(meta, queryKeyInfo).String() + msg := fmt.Sprintf("%s.%s %s %s", alertDetails.Measurement, alertDetails.Field, + alertDetails.Comparator, alerting.RationalizeValueFromUnit(alertDetails.Threshold, evalMatch.Unit, "")) + if len(context.Rule.Message) == 0 { + context.Rule.Message = msg + } return evalMatch, nil } @@ -271,7 +272,8 @@ func (m *meterFetchImp) FetchCustomizeEvalMatch(context *alerting.EvalContext, e if err != nil { return err } - evalMatch.ValueStr = evalMatch.ValueStr + " " + meterCustomizeConfig.UnitDesc + //evalMatch.ValueStr = evalMatch.ValueStr + " " + meterCustomizeConfig.UnitDesc + evalMatch.Unit = meterCustomizeConfig.UnitDesc return nil } diff --git a/pkg/monitor/alerting/notifiers/base.go b/pkg/monitor/alerting/notifiers/base.go index c0e7547201..5ed5d3f395 100644 --- a/pkg/monitor/alerting/notifiers/base.go +++ b/pkg/monitor/alerting/notifiers/base.go @@ -103,11 +103,6 @@ func (n *NotifierBase) ShouldNotify(_ context.Context, evalCtx *alerting.EvalCon return false } - // Do not notify when we become Pending for the first - if prevState == monitor.AlertStatePending && newState == monitor.AlertStatePending { - return false - } - // Do not notify when we become OK from pending if prevState == monitor.AlertStatePending && okOrPending { return false diff --git a/pkg/monitor/models/alertrecord.go b/pkg/monitor/models/alertrecord.go index 6c83a92f8c..8f7da97ad2 100644 --- a/pkg/monitor/models/alertrecord.go +++ b/pkg/monitor/models/alertrecord.go @@ -358,8 +358,15 @@ func (manager *SAlertRecordManager) getNowAlertingRecord(ctx context.Context, us input monitor.AlertRecordListInput) ([]SAlertRecord, error) { //now := time.Now() //startTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 1, now.Location()) + ownerId, err := manager.FetchOwnerId(context.Background(), jsonutils.Marshal(&input)) + if err != nil { + return nil, errors.Wrap(err, "FetchOwnerId error") + } + if ownerId == nil { + ownerId = userCred + } query := manager.Query() - query = manager.FilterByOwner(query, userCred, rbacutils.String2Scope(input.Scope)) + query = manager.FilterByOwner(query, ownerId, rbacutils.String2Scope(input.Scope)) //query = query.GE("created_at", startTime.UTC().Format(timeutils.MysqlTimeFormat)) query = query.Equals("state", monitor.AlertStateAlerting) query = query.IsNotNull("res_type").IsNotEmpty("res_type").Desc("created_at") @@ -373,7 +380,7 @@ func (manager *SAlertRecordManager) getNowAlertingRecord(ctx context.Context, us alertsQuery = CommonAlertManager.FilterByOwner(alertsQuery, userCred, rbacutils.String2Scope(input.Scope)) alerts := make([]SCommonAlert, 0) records := make([]SAlertRecord, 0) - err := db.FetchModelObjects(CommonAlertManager, alertsQuery, &alerts) + err = db.FetchModelObjects(CommonAlertManager, alertsQuery, &alerts) if err != nil { return nil, err } diff --git a/pkg/monitor/models/commonalert.go b/pkg/monitor/models/commonalert.go index e135799d37..d7b9482c12 100644 --- a/pkg/monitor/models/commonalert.go +++ b/pkg/monitor/models/commonalert.go @@ -487,6 +487,9 @@ func (alert *SCommonAlert) GetMoreDetails(ctx context.Context, out monitor.Commo if settings.Channel != monitor.DEFAULT_SEND_NOTIFY_CHANNEL { channel.Insert(settings.Channel) } + if noti.Frequency != 0 { + out.SilentPeriod = fmt.Sprintf("%dm", noti.Frequency/60) + } } out.Channel = channel.List() out.Status = alert.GetStatus() @@ -937,10 +940,10 @@ func (alert *SCommonAlert) customizeDeleteNotis( if err := conf.CustomizeDelete(ctx, userCred, query, data); err != nil { return err } - if err := conf.Delete(ctx, userCred); err != nil { + if err := noti.Detach(ctx, userCred); err != nil { return err } - if err := noti.Detach(ctx, userCred); err != nil { + if err := conf.Delete(ctx, userCred); err != nil { return err } } diff --git a/pkg/monitor/models/notification.go b/pkg/monitor/models/notification.go index 2bb17089d5..7966a4e862 100644 --- a/pkg/monitor/models/notification.go +++ b/pkg/monitor/models/notification.go @@ -229,7 +229,7 @@ func (n *SNotification) ShouldSendNotification() bool { if n.Frequency == 0 { return true } - if int64(time.Now().Sub(n.LastSendNotification)/time.Second) >= n.Frequency { + if int64(time.Now().Sub(n.LastSendNotification)/time.Second)+int64(time.Second*60) >= n.Frequency { return true } return false