common alert bugfix/3.4

This commit is contained in:
zhaoxiangchun
2020-09-21 10:23:57 +08:00
parent b398456ee6
commit f58bca93db
30 changed files with 364 additions and 37 deletions

View File

@@ -0,0 +1,17 @@
## {{.title}}
- Time: {{.start_time}}
- Level: {{.level}}
{{ range .matches}}
- Metric: {{.metric}}
- Trigger value: {{.value_str}}
### Trigger condition:
- {{ $.description}}
### Label
> Name: {{ index .tags "name" }}
> IP: {{ index .tags "ip" }}
> Brand: {{ index .tags "brand" }}
------
{{- end}}

View File

@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{.Title}}</title>
</head>
<style>
.title {
height: 40px;
line-height: 40px;
width: 960px;
background-color: #4da1ff;
color: #fff;
font-size: 16px;
text-align: center;
margin: 0 auto;
}
.table {
width: 960px;
margin: 0 auto;
padding: 10px 30px 0px 30px;
font-family:'微软雅黑',Helvetica,Arial,sans-serif;
font-size:14px;
background-color: #fbfbfb;
}
.tr-title {
height: 10px;
border-left: 5px solid #4da1ff;
margin-left: 10px;
padding: 3px 8px;
font-weight: bold;
}
.td {
width: 80px;
padding-left: 20px;
height: 35px;
font-weight: 400;
}
.link {
text-decoration: none;
color: #3591FF;
}
.thead-tr td {
border-left: 1px solid #d7d7d7;
border-top: 1px solid #d7d7d7;
height: 32px;
font-size: 14px;
background-color: #d7d7d7;
text-align: center;
}
.tbody-tr td {
border-left: 1px solid #d7d7d7;
border-top: 1px solid #d7d7d7;
height: 32px;
font-size: 14px;
font-weight: 400;
text-align: center;
}
.pb-3 {
padding-bottom: 30px;
}
.resouce-table {
width: 98%;
color: #717171;
border-right: 1px solid #d7d7d7;
border-bottom: 1px solid #d7d7d7;
}
</style>
<body>
<h3 class="title">Alert</h3>
<table border="0" cellspacing="0" cellpadding="0" class="table">
<tr><td colspan="4" class="tr-title">Alert info</td></tr>
<tr><td style="height: 10px;"></td></tr>
<tr>
<td class="td">Alarm strategy</td>
<td>{{.name}}</td>
</tr>
<tr>
<td class="td">Alarm level</td>
<td>{{.level}}</td>
</tr>
<tr>
<td class="td">Alarm time</td>
<td>{{.start_time}}</td>
</tr>
<tr>
<td class="td">Strategy details</td>
<td>{{.description}}</td>
</tr>
</table>
<table class="table" style="padding-top: 6px; padding-bottom: 10px;">
<tr>
<td style="padding-left: 20px; font-size: 14px;">To view details<a class="link" target="_blank" href="{{.web_url}}/commonalerts
">Please log in to the platform to view</a></td>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0" class="table pb-3">
<tr><td colspan="4" class="tr-title">Alarm resources</td></tr>
<tr><td style="height: 10px;"></td></tr>
<tr>
<td colspan="4" style="padding: 10px 0 0 20px;">
<table cellspacing="0" cellpadding="0" class="resouce-table">
<thead>
<tr class="thead-tr">
<td>Name</td>
<td>IP</td>
<td>Brand</td>
<td>Trigger value</td>
</tr>
</thead>
<tbody>
{{- range $i, $Matche := .matches}}
<tr class="tbody-tr">
<td>
{{- index .tags "name"}}
</td>
<td>
{{ index .tags "ip" }}
</td>
<td>
{{ index .tags "brand" }}
</td>
<td>{{$Matche.value_str}}</td>
</tr>
{{end}}
</tbody>
</table>
</td>
</tr>
<tr><td style="height: 10px;"></td></tr>
</table>
<p style="width: 960px; height: 40px; line-height: 40px; margin: 0 auto; background-color: #4da1ff; color: #fff; font-size: 12px; text-align: center; ">This email is sent automatically by the system. Please do not reply directly</p>
</body>
</html>

View File

@@ -0,0 +1,17 @@
## {{.title}}
- Time: {{.start_time}}
- Level: {{.level}}
{{ range .matches}}
- Metric: {{.metric}}
- Trigger value: {{.value_str}}
### Trigger condition:
- {{ $.description}}
### Label
> Name: {{ index .tags "name" }}
> IP: {{ index .tags "ip" }}
> Brand: {{ index .tags "brand" }}
------
{{- end}}

View File

@@ -0,0 +1,17 @@
## {{.title}}
- Time: {{.start_time}}
- Level: {{.level}}
{{ range .matches}}
- Metric: {{.metric}}
- Trigger value: {{.value_str}}
### Trigger condition:
- {{ $.description}}
### Label
> Name: {{ index .tags "name" }}
> IP: {{ index .tags "ip" }}
> Brand: {{ index .tags "brand" }}
------
{{- end}}

View File

@@ -0,0 +1,17 @@
## {{.title}}
- Time: {{.start_time}}
- Level: {{.level}}
{{ range .matches}}
- Metric: {{.metric}}
- Trigger value: {{.value_str}}
### Trigger condition:
- {{ $.description}}
### Label
> Name: {{ index .tags "name" }}
> IP: {{ index .tags "ip" }}
> Brand: {{ index .tags "brand" }}
------
{{- end}}

View File

@@ -0,0 +1,17 @@
## {{.title}}
- Time: {{.start_time}}
- Level: {{.level}}
{{ range .matches}}
- Metric: {{.metric}}
- Trigger value: {{.value_str}}
### Trigger condition:
- {{ $.description}}
### Label
> Name: {{ index .tags "name" }}
> IP: {{ index .tags "ip" }}
> Brand: {{ index .tags "brand" }}
------
{{- end}}

View File

@@ -92,11 +92,14 @@ func (c *QueryCondition) filterTags(tags map[string]string, details monitor.Comm
ret["name"] = val
}
}
if strings.Contains(key, "ip") {
if strings.Contains(key, "ip") && key != "host_ip" {
ret["ip"] = val
}
ret[key] = val
}
if _, ok := ret["ip"]; !ok {
ret["ip"] = tags["host_ip"]
}
for _, tag := range []string{"brand", "platform", "hypervisor"} {
if val, ok := ret[tag]; ok {
ret["brand"] = val

View File

@@ -18,6 +18,8 @@ import (
"context"
"fmt"
"golang.org/x/text/language"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
@@ -26,6 +28,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/i18n"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
@@ -113,10 +116,49 @@ func GetNotifyTemplateConfig(ctx *alerting.EvalContext) monitor.NotificationTemp
return config
}
func GetNotifyTemplateConfigOfEN(ctx *alerting.EvalContext) monitor.NotificationTemplateConfig {
priority := notify.NotifyPriorityNormal
level := "Normal"
switch ctx.Rule.Level {
case "", "normal":
priority = notify.NotifyPriorityNormal
case "important":
priority = notify.NotifyPriorityImportant
level = "Important"
case "fatal", "critical":
priority = notify.NotifyPriorityCritical
level = "Critical"
}
topic := fmt.Sprintf("[%s]", level)
isRecovery := false
if ctx.Rule.State == monitor.AlertStateOK {
isRecovery = true
topic = fmt.Sprintf("%s %s Alarm recovered", topic, ctx.GetRuleTitle())
} else if ctx.NoDataFound {
topic = fmt.Sprintf("%s %s No data available", topic, ctx.GetRuleTitle())
} else {
topic = fmt.Sprintf("%s %s Alarm", topic, ctx.GetRuleTitle())
}
config := ctx.GetNotificationTemplateConfig()
config.Title = topic
config.Level = level
config.Priority = string(priority)
config.IsRecovery = isRecovery
return config
}
// Notify sends the alert notification.
func (oc *OneCloudNotifier) Notify(ctx *alerting.EvalContext, _ jsonutils.JSONObject) error {
log.Infof("Sending alert notification %s to onecloud", ctx.GetRuleTitle())
config := GetNotifyTemplateConfig(ctx)
var config monitor.NotificationTemplateConfig
lang := i18n.Lang(ctx.Ctx)
switch lang {
case language.English:
config = GetNotifyTemplateConfigOfEN(ctx)
default:
config = GetNotifyTemplateConfig(ctx)
}
contentConfig := oc.buildContent(config)
var content string

View File

@@ -120,7 +120,7 @@ type SAlert struct {
Settings jsonutils.JSONObject `nullable:"false" list:"user" create:"required" update:"user"`
Level string `charset:"ascii" width:"36" nullable:"false" default:"normal" list:"user" update:"user"`
Message string `charset:"utf8" list:"user" create:"optional" update:"user"`
UsedBy string `charset:"ascii" list:"user"`
UsedBy string `charset:"ascii" create:"optional" list:"user"`
// Silenced bool
ExecutionError string `charset:"utf8" list:"user"`

View File

@@ -13,6 +13,7 @@ import (
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/apis/monitor"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
@@ -529,9 +530,6 @@ func getCommonAlertMetricDetailsFromCondition(cond *monitor.AlertCondition,
//fill measurement\field desciption info
getMetricDescriptionDetails(metricDetails)
if metricDetails.FieldOpt == "/" {
metricDetails.FieldDescription.Unit = ""
}
}
func getMetricDescriptionDetails(metricDetails *monitor.CommonAlertMetricDetails) {
@@ -557,15 +555,24 @@ func getMetricDescriptionDetails(metricDetails *monitor.CommonAlertMetricDetails
return
}
if fieldDes, ok := influxdbMeasurements[0].FieldDescriptions[field]; ok {
if len(metricDetails.FieldOpt) != 0 {
fieldDes.Name = metricDetails.Field
fieldDes.DisplayName = ""
}
metricDetails.FieldDescription = fieldDes
if len(metricDetails.FieldOpt) != 0 {
metricDetails.FieldDescription.Name = metricDetails.Field
metricDetails.FieldDescription.DisplayName = metricDetails.Field
getExtraFieldDetails(metricDetails)
break
}
}
}
}
func getExtraFieldDetails(metricDetails *monitor.CommonAlertMetricDetails) {
if metricDetails.FieldOpt == monitor.CommonAlertFieldOpt_Division && metricDetails.Threshold < float64(1) {
metricDetails.Threshold = metricDetails.Threshold * float64(100)
metricDetails.FieldDescription.Unit = "%"
}
}
func getQueryEvalType(evalType string) string {
typ := ""
switch evalType {
@@ -586,11 +593,12 @@ func (man *SCommonAlertManager) toAlertCreatInput(input monitor.CommonAlertCreat
//ret.Settings =monitor.AlertSetting{}
for _, metricquery := range input.CommonMetricInputQuery.MetricQuery {
condition := monitor.AlertCondition{
Type: "query",
Query: *metricquery.AlertQuery,
Reducer: monitor.Condition{Type: metricquery.Reduce},
Evaluator: monitor.Condition{Type: getQueryEvalType(metricquery.Comparator), Params: []float64{metricquery.Threshold}},
Operator: "and",
Type: "query",
Query: *metricquery.AlertQuery,
Reducer: monitor.Condition{Type: metricquery.Reduce},
Evaluator: monitor.Condition{Type: getQueryEvalType(metricquery.Comparator),
Params: []float64{fieldOperatorThreshold(metricquery.FieldOpt, metricquery.Threshold)}},
Operator: "and",
}
if metricquery.FieldOpt != "" {
condition.Reducer.Operators = []string{metricquery.FieldOpt}
@@ -600,12 +608,33 @@ func (man *SCommonAlertManager) toAlertCreatInput(input monitor.CommonAlertCreat
return *ret
}
func fieldOperatorThreshold(opt string, threshold float64) float64 {
if opt == monitor.CommonAlertFieldOpt_Division && threshold > 1 {
return threshold / float64(100)
}
return threshold
}
func (alert *SCommonAlert) ValidateUpdateData(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data *jsonutils.JSONDict,
) (*jsonutils.JSONDict, error) {
generateName, _ := data.GetString("generate_name")
if len(generateName) != 0 && alert.Name != generateName {
name, err := db.GenerateName(CommonAlertManager, userCred, generateName)
if err != nil {
return data, err
}
data.Set("name", jsonutils.NewString(name))
}
statusUpdate := apis.StatusStandaloneResourceBaseUpdateInput{}
data.Unmarshal(&statusUpdate)
_, err := alert.SAlert.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, statusUpdate)
if err != nil {
return data, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
}
updataInput := new(monitor.CommonAlertUpdateInput)
if period, _ := data.GetString("period"); len(period) > 0 {
if _, err := time.ParseDuration(period); err != nil {
@@ -686,7 +715,9 @@ func (alert *SCommonAlert) PostUpdate(
if err := alert.UpdateNotification(ctx, userCred, query, data); err != nil {
log.Errorln("update notification", err)
}
_, err := alert.PerformSetScope(ctx, userCred, query, data)
}
if _, err := data.GetString("scope"); err == nil {
_, err = alert.PerformSetScope(ctx, userCred, query, data)
if err != nil {
log.Errorln(errors.Wrap(err, "Alert PerformSetScope"))
}
@@ -784,6 +815,23 @@ func (alert *SCommonAlert) AllowPerformSetScope(ctx context.Context, userCred mc
}
func (alert *SCommonAlert) PerformSetScope(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
domainId := jsonutils.GetAnyString(data, []string{"domain_id", "domain", "project_domain_id", "project_domain"})
projectId := jsonutils.GetAnyString(data, []string{"project_id", "project"})
if len(domainId) == 0 && len(projectId) == 0 {
scope, _ := data.GetString("scope")
if len(scope) != 0 {
switch rbacutils.TRbacScope(scope) {
case rbacutils.ScopeSystem:
case rbacutils.ScopeDomain:
domainId = userCred.GetProjectDomainId()
data.(*jsonutils.JSONDict).Set("domain_id", jsonutils.NewString(domainId))
case rbacutils.ScopeProject:
projectId = userCred.GetProjectId()
data.(*jsonutils.JSONDict).Set("project_id", jsonutils.NewString(projectId))
}
}
}
return db.PerformSetScope(ctx, alert, userCred, data)
}

View File

@@ -341,19 +341,19 @@ func (self *SDataSourceManager) getMetricDescriptions(influxdbMeasurements []mon
if err != nil {
log.Errorln(errors.Wrap(err, "DataSourceManager getMetricDescriptions error"))
}
measurements, err := MetricMeasurementManager.getMeasurement(query)
if len(measurements) != 0 {
descriMeasurements, err := MetricMeasurementManager.getMeasurement(query)
if len(descriMeasurements) != 0 {
measurementsIns := make([]interface{}, len(measurements))
for i, _ := range measurements {
measurementsIns[i] = &measurements[i]
measurementsIns := make([]interface{}, len(descriMeasurements))
for i, _ := range descriMeasurements {
measurementsIns[i] = &descriMeasurements[i]
}
details := MetricMeasurementManager.FetchCustomizeColumns(context.Background(), userCred, jsonutils.NewDict(), measurementsIns,
stringutils2.NewSortedStrings([]string{}), true)
if err != nil {
log.Errorln(errors.Wrap(err, "DataSourceManager getMetricDescriptions error"))
}
for i, measureDes := range measurements {
for i, measureDes := range descriMeasurements {
for j, _ := range influxdbMeasurements {
if measureDes.Name == influxdbMeasurements[j].Measurement {
if len(measureDes.DisplayName) != 0 {
@@ -495,13 +495,12 @@ func (self *SDataSourceManager) getFilterMeasurement(queryChan *influxdbQueryCha
if value[i] == nil {
continue
}
floatVal, err := value[i].Float()
_, err := value[i].Float()
if err != nil {
continue
}
if !floatEquals(floatVal, float64(0)) {
containsVal = true
}
containsVal = true
break
}
if containsVal {
rtnFields = append(rtnFields, strings.Replace(meanFieldArr[i], "last_", "", 1))
@@ -820,6 +819,7 @@ func (self *SDataSourceManager) getFilterMeasurementTagValue(tagValueChan *influ
buffer.WriteString(fmt.Sprintf(` AND %s `, tagFilter))
}
buffer.WriteString(fmt.Sprintf(` GROUP BY %q`, tagKey))
log.Errorln(buffer.String())
rtn, err := db.Query(buffer.String())
if err != nil {
return errors.Wrap(err, "getFilterMeasurementTagValue query error")

View File

@@ -59,22 +59,21 @@ func (self *SUnifiedMonitorManager) AllowGetPropertyMeasurements(ctx context.Con
func (self *SUnifiedMonitorManager) GetPropertyMeasurements(ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
filter, err := getTagFilterByRequestQuery(ctx, query)
filter, err := getTagFilterByRequestQuery(ctx, userCred, query)
if err != nil {
return nil, err
}
return DataSourceManager.GetMeasurementsWithDescriptionInfos(query, "", filter)
}
func getTagFilterByRequestQuery(ctx context.Context, query jsonutils.JSONObject) (filter string, err error) {
func getTagFilterByRequestQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (filter string, err error) {
if scope, err := query.GetString("scope"); err == nil {
filter, err = filterByScope(ctx, scope, query)
}
scope, _ := query.GetString("scope")
filter, err = filterByScope(ctx, userCred, scope, query)
return
}
func filterByScope(ctx context.Context, scope string, data jsonutils.JSONObject) (string, error) {
func filterByScope(ctx context.Context, userCred mcclient.TokenCredential, scope string, data jsonutils.JSONObject) (string, error) {
domainId := jsonutils.GetAnyString(data, []string{"domain_id", "domain", "project_domain_id", "project_domain"})
projectId := jsonutils.GetAnyString(data, []string{"project_id", "project"})
if projectId != "" {
@@ -98,17 +97,16 @@ func filterByScope(ctx context.Context, scope string, data jsonutils.JSONObject)
return "", nil
case "domain":
if domainId == "" {
return "", fmt.Errorf("scope is domain but domainId is null")
domainId = userCred.GetProjectDomainId()
}
return getProjectIdsFilterByDomain(domainId)
case "project":
default:
if projectId == "" {
return "", fmt.Errorf("scope is project but projectId is null")
projectId = userCred.GetProjectId()
}
return getProjectIdFilterByProject(projectId)
}
return "", fmt.Errorf("scope is illegal")
}
func getTenantIdStr(role string, userCred mcclient.TokenCredential) (string, error) {
@@ -167,7 +165,7 @@ func (self *SUnifiedMonitorManager) GetPropertyMetricMeasurement(ctx context.Con
GroupOptType: monitor.UNIFIED_MONITOR_GROUPBY_OPT_TYPE,
GroupOptValue: monitor.UNIFIED_MONITOR_GROUPBY_OPT_VALUE,
}
filter, err := getTagFilterByRequestQuery(ctx, query)
filter, err := getTagFilterByRequestQuery(ctx, userCred, query)
if err != nil {
return nil, err
}