mirror of https://github.com/grafana/grafana
feat(alerting): progress on alerting UI and model, refactoring of dashboard parser and tests into extractor component, moved tests from sqlstore to alerting package
parent
1fa9ae810b
commit
2b4a9954b1
@ -0,0 +1,76 @@ |
||||
package alerting |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson" |
||||
|
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
type AlertRule struct { |
||||
Id int64 |
||||
OrgId int64 |
||||
DashboardId int64 |
||||
PanelId int64 |
||||
Frequency int64 |
||||
Name string |
||||
Description string |
||||
State string |
||||
Warning Level |
||||
Critical Level |
||||
Query AlertQuery |
||||
Transform string |
||||
TransformParams simplejson.Json |
||||
Transformer Transformer |
||||
} |
||||
|
||||
func NewAlertRuleFromDBModel(ruleDef *m.AlertRuleModel) (*AlertRule, error) { |
||||
model := &AlertRule{} |
||||
model.Id = ruleDef.Id |
||||
model.OrgId = ruleDef.OrgId |
||||
model.Name = ruleDef.Name |
||||
model.Description = ruleDef.Description |
||||
model.State = ruleDef.State |
||||
|
||||
critical := ruleDef.Expression.Get("critical") |
||||
model.Critical = Level{ |
||||
Operator: critical.Get("op").MustString(), |
||||
Level: critical.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
warning := ruleDef.Expression.Get("warning") |
||||
model.Warning = Level{ |
||||
Operator: warning.Get("op").MustString(), |
||||
Level: warning.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
model.Frequency = ruleDef.Expression.Get("frequency").MustInt64() |
||||
model.Transform = ruleDef.Expression.Get("transform").Get("type").MustString() |
||||
model.TransformParams = *ruleDef.Expression.Get("transform") |
||||
|
||||
if model.Transform == "aggregation" { |
||||
model.Transformer = &AggregationTransformer{ |
||||
Method: ruleDef.Expression.Get("transform").Get("method").MustString(), |
||||
} |
||||
} |
||||
|
||||
query := ruleDef.Expression.Get("query") |
||||
model.Query = AlertQuery{ |
||||
Query: query.Get("query").MustString(), |
||||
DatasourceId: query.Get("datasourceId").MustInt64(), |
||||
From: query.Get("from").MustString(), |
||||
To: query.Get("to").MustString(), |
||||
Aggregator: query.Get("agg").MustString(), |
||||
} |
||||
|
||||
if model.Query.Query == "" { |
||||
return nil, fmt.Errorf("missing query.query") |
||||
} |
||||
|
||||
if model.Query.DatasourceId == 0 { |
||||
return nil, fmt.Errorf("missing query.datasourceId") |
||||
} |
||||
|
||||
return model, nil |
||||
} |
||||
@ -0,0 +1,89 @@ |
||||
package alerting |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
type UpdateDashboardAlertsCommand struct { |
||||
UserId int64 |
||||
OrgId int64 |
||||
Dashboard *m.Dashboard |
||||
} |
||||
|
||||
func init() { |
||||
bus.AddHandler("alerting", updateDashboardAlerts) |
||||
} |
||||
|
||||
func updateDashboardAlerts(cmd *UpdateDashboardAlertsCommand) error { |
||||
saveRulesCmd := m.SaveAlertsCommand{ |
||||
OrgId: cmd.OrgId, |
||||
UserId: cmd.UserId, |
||||
} |
||||
|
||||
extractor := NewAlertRuleExtractor(cmd.Dashboard, cmd.OrgId) |
||||
|
||||
rules, err := extractor.GetRuleModels() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
saveRulesCmd.Alerts = rules |
||||
if bus.Dispatch(&saveRulesCmd); err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func ConvetAlertModelToAlertRule(ruleDef *m.AlertRuleModel) (*AlertRule, error) { |
||||
model := &AlertRule{} |
||||
model.Id = ruleDef.Id |
||||
model.OrgId = ruleDef.OrgId |
||||
model.Name = ruleDef.Name |
||||
model.Description = ruleDef.Description |
||||
model.State = ruleDef.State |
||||
|
||||
critical := ruleDef.Expression.Get("critical") |
||||
model.Critical = Level{ |
||||
Operator: critical.Get("op").MustString(), |
||||
Level: critical.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
warning := ruleDef.Expression.Get("warning") |
||||
model.Warning = Level{ |
||||
Operator: warning.Get("op").MustString(), |
||||
Level: warning.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
model.Frequency = ruleDef.Expression.Get("frequency").MustInt64() |
||||
model.Transform = ruleDef.Expression.Get("transform").Get("type").MustString() |
||||
model.TransformParams = *ruleDef.Expression.Get("transform") |
||||
|
||||
if model.Transform == "aggregation" { |
||||
model.Transformer = &AggregationTransformer{ |
||||
Method: ruleDef.Expression.Get("transform").Get("method").MustString(), |
||||
} |
||||
} |
||||
|
||||
query := ruleDef.Expression.Get("query") |
||||
model.Query = AlertQuery{ |
||||
Query: query.Get("query").MustString(), |
||||
DatasourceId: query.Get("datasourceId").MustInt64(), |
||||
From: query.Get("from").MustString(), |
||||
To: query.Get("to").MustString(), |
||||
Aggregator: query.Get("agg").MustString(), |
||||
} |
||||
|
||||
if model.Query.Query == "" { |
||||
return nil, fmt.Errorf("missing query.query") |
||||
} |
||||
|
||||
if model.Query.DatasourceId == 0 { |
||||
return nil, fmt.Errorf("missing query.datasourceId") |
||||
} |
||||
|
||||
return model, nil |
||||
} |
||||
@ -1,135 +0,0 @@ |
||||
package alerting |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/components/simplejson" |
||||
"github.com/grafana/grafana/pkg/log" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRuleModel { |
||||
alerts := make([]*m.AlertRuleModel, 0) |
||||
|
||||
for _, rowObj := range cmd.Dashboard.Get("rows").MustArray() { |
||||
row := simplejson.NewFromAny(rowObj) |
||||
|
||||
for _, panelObj := range row.Get("panels").MustArray() { |
||||
panel := simplejson.NewFromAny(panelObj) |
||||
|
||||
alerting := panel.Get("alerting") |
||||
alert := &m.AlertRuleModel{ |
||||
DashboardId: cmd.Result.Id, |
||||
OrgId: cmd.Result.OrgId, |
||||
PanelId: panel.Get("id").MustInt64(), |
||||
Id: alerting.Get("id").MustInt64(), |
||||
Name: alerting.Get("name").MustString(), |
||||
Description: alerting.Get("description").MustString(), |
||||
} |
||||
|
||||
log.Info("Alertrule: %v", alert.Name) |
||||
|
||||
valueQuery := alerting.Get("query") |
||||
valueQueryRef := valueQuery.Get("refId").MustString() |
||||
for _, targetsObj := range panel.Get("targets").MustArray() { |
||||
target := simplejson.NewFromAny(targetsObj) |
||||
|
||||
if target.Get("refId").MustString() == valueQueryRef { |
||||
datsourceName := "" |
||||
if target.Get("datasource").MustString() != "" { |
||||
datsourceName = target.Get("datasource").MustString() |
||||
} else if panel.Get("datasource").MustString() != "" { |
||||
datsourceName = panel.Get("datasource").MustString() |
||||
} |
||||
|
||||
if datsourceName == "" { |
||||
query := &m.GetDataSourcesQuery{OrgId: cmd.OrgId} |
||||
if err := bus.Dispatch(query); err == nil { |
||||
for _, ds := range query.Result { |
||||
if ds.IsDefault { |
||||
alerting.SetPath([]string{"query", "datasourceId"}, ds.Id) |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
query := &m.GetDataSourceByNameQuery{ |
||||
Name: panel.Get("datasource").MustString(), |
||||
OrgId: cmd.OrgId, |
||||
} |
||||
bus.Dispatch(query) |
||||
alerting.SetPath([]string{"query", "datasourceId"}, query.Result.Id) |
||||
} |
||||
|
||||
targetQuery := target.Get("target").MustString() |
||||
if targetQuery != "" { |
||||
alerting.SetPath([]string{"query", "query"}, targetQuery) |
||||
} |
||||
} |
||||
} |
||||
|
||||
alert.Expression = alerting |
||||
|
||||
_, err := ConvetAlertModelToAlertRule(alert) |
||||
|
||||
if err == nil && alert.ValidToSave() { |
||||
alerts = append(alerts, alert) |
||||
} else { |
||||
log.Error2("Failed to parse model from expression", "error", err) |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
return alerts |
||||
} |
||||
|
||||
func ConvetAlertModelToAlertRule(ruleDef *m.AlertRuleModel) (*AlertRule, error) { |
||||
model := &AlertRule{} |
||||
model.Id = ruleDef.Id |
||||
model.OrgId = ruleDef.OrgId |
||||
model.Name = ruleDef.Name |
||||
model.Description = ruleDef.Description |
||||
model.State = ruleDef.State |
||||
|
||||
critical := ruleDef.Expression.Get("critical") |
||||
model.Critical = Level{ |
||||
Operator: critical.Get("op").MustString(), |
||||
Level: critical.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
warning := ruleDef.Expression.Get("warning") |
||||
model.Warning = Level{ |
||||
Operator: warning.Get("op").MustString(), |
||||
Level: warning.Get("level").MustFloat64(), |
||||
} |
||||
|
||||
model.Frequency = ruleDef.Expression.Get("frequency").MustInt64() |
||||
model.Transform = ruleDef.Expression.Get("transform").Get("type").MustString() |
||||
model.TransformParams = *ruleDef.Expression.Get("transform") |
||||
|
||||
if model.Transform == "aggregation" { |
||||
model.Transformer = &AggregationTransformer{ |
||||
Method: ruleDef.Expression.Get("transform").Get("method").MustString(), |
||||
} |
||||
} |
||||
|
||||
query := ruleDef.Expression.Get("query") |
||||
model.Query = AlertQuery{ |
||||
Query: query.Get("query").MustString(), |
||||
DatasourceId: query.Get("datasourceId").MustInt64(), |
||||
From: query.Get("from").MustString(), |
||||
To: query.Get("to").MustString(), |
||||
Aggregator: query.Get("agg").MustString(), |
||||
} |
||||
|
||||
if model.Query.Query == "" { |
||||
return nil, fmt.Errorf("missing query.query") |
||||
} |
||||
|
||||
if model.Query.DatasourceId == 0 { |
||||
return nil, fmt.Errorf("missing query.datasourceId") |
||||
} |
||||
|
||||
return model, nil |
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
package alerting |
||||
|
||||
import ( |
||||
"errors" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/components/simplejson" |
||||
"github.com/grafana/grafana/pkg/log" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
type AlertRuleExtractor struct { |
||||
Dash *m.Dashboard |
||||
OrgId int64 |
||||
log log.Logger |
||||
} |
||||
|
||||
func NewAlertRuleExtractor(dash *m.Dashboard, orgId int64) *AlertRuleExtractor { |
||||
return &AlertRuleExtractor{ |
||||
Dash: dash, |
||||
OrgId: orgId, |
||||
log: log.New("alerting.extractor"), |
||||
} |
||||
} |
||||
|
||||
func (e *AlertRuleExtractor) lookupDatasourceId(dsName string) (int64, error) { |
||||
if dsName == "" { |
||||
query := &m.GetDataSourcesQuery{OrgId: e.OrgId} |
||||
if err := bus.Dispatch(query); err != nil { |
||||
return 0, err |
||||
} else { |
||||
for _, ds := range query.Result { |
||||
if ds.IsDefault { |
||||
return ds.Id, nil |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
query := &m.GetDataSourceByNameQuery{Name: dsName, OrgId: e.OrgId} |
||||
if err := bus.Dispatch(query); err != nil { |
||||
return 0, err |
||||
} else { |
||||
return query.Result.Id, nil |
||||
} |
||||
} |
||||
|
||||
return 0, errors.New("Could not find datasource id for " + dsName) |
||||
} |
||||
|
||||
func (e *AlertRuleExtractor) GetRuleModels() (m.AlertRules, error) { |
||||
|
||||
rules := make(m.AlertRules, 0) |
||||
|
||||
for _, rowObj := range e.Dash.Data.Get("rows").MustArray() { |
||||
row := simplejson.NewFromAny(rowObj) |
||||
|
||||
for _, panelObj := range row.Get("panels").MustArray() { |
||||
panel := simplejson.NewFromAny(panelObj) |
||||
jsonRule := panel.Get("alerting") |
||||
|
||||
// check if marked for deletion
|
||||
deleted := jsonRule.Get("deleted").MustBool() |
||||
if deleted { |
||||
e.log.Info("Deleted alert rule found") |
||||
continue |
||||
} |
||||
|
||||
ruleModel := &m.AlertRuleModel{ |
||||
DashboardId: e.Dash.Id, |
||||
OrgId: e.OrgId, |
||||
PanelId: panel.Get("id").MustInt64(), |
||||
Id: jsonRule.Get("id").MustInt64(), |
||||
Name: jsonRule.Get("name").MustString(), |
||||
Scheduler: jsonRule.Get("scheduler").MustInt64(), |
||||
Enabled: jsonRule.Get("enabled").MustBool(), |
||||
Description: jsonRule.Get("description").MustString(), |
||||
} |
||||
|
||||
valueQuery := jsonRule.Get("query") |
||||
valueQueryRef := valueQuery.Get("refId").MustString() |
||||
for _, targetsObj := range panel.Get("targets").MustArray() { |
||||
target := simplejson.NewFromAny(targetsObj) |
||||
|
||||
if target.Get("refId").MustString() == valueQueryRef { |
||||
dsName := "" |
||||
if target.Get("datasource").MustString() != "" { |
||||
dsName = target.Get("datasource").MustString() |
||||
} else if panel.Get("datasource").MustString() != "" { |
||||
dsName = panel.Get("datasource").MustString() |
||||
} |
||||
|
||||
if datasourceId, err := e.lookupDatasourceId(dsName); err != nil { |
||||
return nil, err |
||||
} else { |
||||
valueQuery.SetPath([]string{"datasourceId"}, datasourceId) |
||||
} |
||||
|
||||
targetQuery := target.Get("target").MustString() |
||||
if targetQuery != "" { |
||||
jsonRule.SetPath([]string{"query", "query"}, targetQuery) |
||||
} |
||||
} |
||||
} |
||||
|
||||
ruleModel.Expression = jsonRule |
||||
|
||||
// validate
|
||||
_, err := NewAlertRuleFromDBModel(ruleModel) |
||||
if err == nil && ruleModel.ValidToSave() { |
||||
rules = append(rules, ruleModel) |
||||
} else { |
||||
e.log.Error("Failed to extract alert rules from dashboard", "error", err) |
||||
return nil, errors.New("Failed to extract alert rules from dashboard") |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
return rules, nil |
||||
} |
||||
Loading…
Reference in new issue