diff --git a/pkg/models/alerts.go b/pkg/models/alerts.go index e069f7e81c3..0764aaa2845 100644 --- a/pkg/models/alerts.go +++ b/pkg/models/alerts.go @@ -6,7 +6,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" ) -type AlertRule struct { +type AlertRuleDAO struct { Id int64 OrgId int64 DashboardId int64 @@ -21,11 +21,15 @@ type AlertRule struct { Expression *simplejson.Json } -func (alertRule *AlertRule) ValidToSave() bool { - return true +func (this AlertRuleDAO) TableName() string { + return "alert_rule" } -func (this *AlertRule) ContainsUpdates(other *AlertRule) bool { +func (alertRule *AlertRuleDAO) ValidToSave() bool { + return alertRule.DashboardId != 0 +} + +func (this *AlertRuleDAO) ContainsUpdates(other *AlertRuleDAO) bool { result := false result = result || this.Name != other.Name @@ -78,7 +82,7 @@ type SaveAlertsCommand struct { UserId int64 OrgId int64 - Alerts []*AlertRule + Alerts []*AlertRuleDAO } type DeleteAlertCommand struct { @@ -92,23 +96,17 @@ type GetAlertsQuery struct { DashboardId int64 PanelId int64 - Result []*AlertRule + Result []*AlertRuleDAO } type GetAllAlertsQuery struct { - Result []*AlertRule -} - -type GetAlertsForExecutionQuery struct { - Timestamp int64 - - Result []*AlertRule + Result []*AlertRuleDAO } type GetAlertByIdQuery struct { Id int64 - Result *AlertRule + Result *AlertRuleDAO } type GetAlertChangesQuery struct { diff --git a/pkg/models/alerts_state.go b/pkg/models/alerts_state.go index 171eb754412..63f1ac73c0e 100644 --- a/pkg/models/alerts_state.go +++ b/pkg/models/alerts_state.go @@ -31,7 +31,7 @@ type UpdateAlertStateCommand struct { NewState string `json:"newState" binding:"Required"` Info string `json:"info"` - Result *AlertRule + Result *AlertRuleDAO } // Queries diff --git a/pkg/models/alerts_test.go b/pkg/models/alerts_test.go index 33a4937f5ad..af672ca6016 100644 --- a/pkg/models/alerts_test.go +++ b/pkg/models/alerts_test.go @@ -13,13 +13,13 @@ func TestAlertingModelTest(t *testing.T) { json1, _ := simplejson.NewJson([]byte(`{ "field": "value" }`)) json2, _ := simplejson.NewJson([]byte(`{ "field": "value" }`)) - rule1 := &AlertRule{ + rule1 := &AlertRuleDAO{ Expression: json1, Name: "Namn", Description: "Description", } - rule2 := &AlertRule{ + rule2 := &AlertRuleDAO{ Expression: json2, Name: "Namn", Description: "Description", diff --git a/pkg/services/alerting/dashboard_parser.go b/pkg/services/alerting/dashboard_parser.go index c6df8e53a10..7517db5b14c 100644 --- a/pkg/services/alerting/dashboard_parser.go +++ b/pkg/services/alerting/dashboard_parser.go @@ -1,14 +1,16 @@ 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.AlertRule { - alerts := make([]*m.AlertRule, 0) +func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRuleDAO { + alerts := make([]*m.AlertRuleDAO, 0) for _, rowObj := range cmd.Dashboard.Get("rows").MustArray() { row := simplejson.NewFromAny(rowObj) @@ -17,7 +19,7 @@ func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRule { panel := simplejson.NewFromAny(panelObj) alerting := panel.Get("alerting") - alert := &m.AlertRule{ + alert := &m.AlertRuleDAO{ DashboardId: cmd.Result.Id, OrgId: cmd.Result.OrgId, PanelId: panel.Get("id").MustInt64(), @@ -28,8 +30,7 @@ func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRule { log.Info("Alertrule: %v", alert.Name) - expression := alerting - valueQuery := expression.Get("valueQuery") + valueQuery := alerting.Get("valueQuery") valueQueryRef := valueQuery.Get("queryRefId").MustString() for _, targetsObj := range panel.Get("targets").MustArray() { target := simplejson.NewFromAny(targetsObj) @@ -47,7 +48,7 @@ func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRule { if err := bus.Dispatch(query); err == nil { for _, ds := range query.Result { if ds.IsDefault { - valueQuery.Set("datasourceId", ds.Id) + alerting.SetPath([]string{"valueQuery", "datasourceId"}, ds.Id) } } } @@ -57,59 +58,71 @@ func ParseAlertsFromDashboard(cmd *m.SaveDashboardCommand) []*m.AlertRule { OrgId: cmd.OrgId, } bus.Dispatch(query) - valueQuery.Set("datasourceId", query.Result.Id) + alerting.SetPath([]string{"valueQuery", "datasourceId"}, query.Result.Id) } targetQuery := target.Get("target").MustString() if targetQuery != "" { - valueQuery.Set("query", targetQuery) + alerting.SetPath([]string{"valueQuery", "query"}, targetQuery) } } } - expression.Set("valueQuery", valueQuery) - alert.Expression = expression + alert.Expression = alerting - alertRule := &AlertRule{} + _, err := ParseAlertRulesFromAlertModel(alert) - ParseAlertRulesFromAlertModel(alert, alertRule) - - if alert.ValidToSave() && alertRule.IsValid() { + if err == nil && alert.ValidToSave() { alerts = append(alerts, alert) + } else { + log.Error2("Failed to parse model from expression", "error", err) } + } } return alerts } -func (rule *AlertRule) IsValid() bool { - return rule.ValueQuery.Query != "" -} +func ParseAlertRulesFromAlertModel(ruleDef *m.AlertRuleDAO) (*AlertRule, error) { + model := &AlertRule{} + model.Id = ruleDef.Id + model.OrgId = ruleDef.OrgId + model.Name = ruleDef.Name + model.Description = ruleDef.Description + model.State = ruleDef.State -func ParseAlertRulesFromAlertModel(ruleDef *m.AlertRule, model *AlertRule) error { critical := ruleDef.Expression.Get("critical") model.Critical = Level{ - Operator: critical.Get("operator").MustString(), + Operator: critical.Get("op").MustString(), Level: critical.Get("level").MustFloat64(), } warning := ruleDef.Expression.Get("warning") model.Warning = Level{ - Operator: warning.Get("operator").MustString(), + Operator: warning.Get("op").MustString(), Level: warning.Get("level").MustFloat64(), } model.Frequency = ruleDef.Expression.Get("frequency").MustInt64() valueQuery := ruleDef.Expression.Get("valueQuery") + model.ValueQuery = AlertQuery{ Query: valueQuery.Get("query").MustString(), DatasourceId: valueQuery.Get("datasourceId").MustInt64(), - From: valueQuery.Get("From").MustInt64(), - Until: valueQuery.Get("until").MustInt64(), - Aggregator: valueQuery.Get("aggregator").MustString(), + From: valueQuery.Get("from").MustString(), + To: valueQuery.Get("to").MustString(), + Aggregator: valueQuery.Get("agg").MustString(), } - return nil + if model.ValueQuery.Query == "" { + return nil, fmt.Errorf("missing valueQuery query") + } + + if model.ValueQuery.DatasourceId == 0 { + return nil, fmt.Errorf("missing valueQuery datasourceId") + } + + return model, nil } diff --git a/pkg/services/alerting/executor.go b/pkg/services/alerting/executor.go index e676c318e92..ea460a64798 100644 --- a/pkg/services/alerting/executor.go +++ b/pkg/services/alerting/executor.go @@ -2,7 +2,6 @@ package alerting import ( "fmt" - "strconv" "math" @@ -14,7 +13,6 @@ import ( ) var ( - resultLogFmt = "Alerting: executor %s %1.2f %s %1.2f : %v" descriptionFmt = "Actual value: %1.2f for %s" ) @@ -102,7 +100,7 @@ func (e *ExecutorImpl) Execute(job *AlertJob, resultQueue chan *AlertResult) { func (e *ExecutorImpl) executeQuery(job *AlertJob) (tsdb.TimeSeriesSlice, error) { getDsInfo := &m.GetDataSourceByIdQuery{ - Id: 1, + Id: job.Rule.ValueQuery.DatasourceId, OrgId: job.Rule.OrgId, } @@ -130,15 +128,16 @@ func (e *ExecutorImpl) executeQuery(job *AlertJob) (tsdb.TimeSeriesSlice, error) } func (e *ExecutorImpl) GetRequestForAlertRule(rule *AlertRule, datasource *m.DataSource) *tsdb.Request { + log.Debug2("GetRequest", "query", rule.ValueQuery.Query, "from", rule.ValueQuery.From, "datasourceId", datasource.Id) req := &tsdb.Request{ TimeRange: tsdb.TimeRange{ - From: "-" + strconv.Itoa(int(rule.ValueQuery.From)) + "s", - To: "now", + From: "-" + rule.ValueQuery.From, + To: rule.ValueQuery.To, }, Queries: []*tsdb.Query{ { RefId: "A", - Query: "apps.fakesite.*.counters.requests.count", + Query: rule.ValueQuery.Query, DataSource: &tsdb.DataSourceInfo{ Id: datasource.Id, Name: datasource.Name, @@ -156,7 +155,7 @@ func (e *ExecutorImpl) evaluateRule(rule *AlertRule, series tsdb.TimeSeriesSlice e.log.Debug("Evaluating Alerting Rule", "seriesCount", len(series), "ruleName", rule.Name) for _, serie := range series { - log.Debug("Evaluating series", "series", serie.Name) + e.log.Debug("Evaluating series", "series", serie.Name) if aggregator["avg"] == nil { continue @@ -166,7 +165,7 @@ func (e *ExecutorImpl) evaluateRule(rule *AlertRule, series tsdb.TimeSeriesSlice var critOperartor = operators[rule.Critical.Operator] var critResult = critOperartor(aggValue, rule.Critical.Level) - log.Trace(resultLogFmt, "Crit", serie.Name, aggValue, rule.Critical.Operator, rule.Critical.Level, critResult) + e.log.Debug("Alert execution Crit", "name", serie.Name, "aggValue", aggValue, "operator", rule.Critical.Operator, "level", rule.Critical.Level, "result", critResult) if critResult { return &AlertResult{ State: alertstates.Critical, @@ -177,7 +176,7 @@ func (e *ExecutorImpl) evaluateRule(rule *AlertRule, series tsdb.TimeSeriesSlice var warnOperartor = operators[rule.Warning.Operator] var warnResult = warnOperartor(aggValue, rule.Warning.Level) - log.Trace(resultLogFmt, "Warn", serie.Name, aggValue, rule.Warning.Operator, rule.Warning.Level, warnResult) + e.log.Debug("Alert execution Warn", "name", serie.Name, "aggValue", aggValue, "operator", rule.Warning.Operator, "level", rule.Warning.Level, "result", warnResult) if warnResult { return &AlertResult{ State: alertstates.Warn, diff --git a/pkg/services/alerting/models.go b/pkg/services/alerting/models.go index afbefac3658..88dc22feaf7 100644 --- a/pkg/services/alerting/models.go +++ b/pkg/services/alerting/models.go @@ -49,6 +49,6 @@ type AlertQuery struct { Query string DatasourceId int64 Aggregator string - From int64 - Until int64 + From string + To string } diff --git a/pkg/services/alerting/rule_reader.go b/pkg/services/alerting/rule_reader.go index c8c3a73cc55..710e695903d 100644 --- a/pkg/services/alerting/rule_reader.go +++ b/pkg/services/alerting/rule_reader.go @@ -49,15 +49,7 @@ func (arr *AlertRuleReader) Fetch() []*AlertRule { res := make([]*AlertRule, len(cmd.Result)) for i, ruleDef := range cmd.Result { - model := &AlertRule{} - model.Id = ruleDef.Id - model.OrgId = ruleDef.OrgId - model.Name = ruleDef.Name - model.Description = ruleDef.Description - model.State = ruleDef.State - - ParseAlertRulesFromAlertModel(ruleDef, model) - + model, _ := ParseAlertRulesFromAlertModel(ruleDef) res[i] = model } diff --git a/pkg/services/alerting/scheduler.go b/pkg/services/alerting/scheduler.go index ffa2b2b900c..b013b8a3eeb 100644 --- a/pkg/services/alerting/scheduler.go +++ b/pkg/services/alerting/scheduler.go @@ -8,16 +8,18 @@ import ( type SchedulerImpl struct { jobs map[int64]*AlertJob + log log.Logger } func NewScheduler() Scheduler { return &SchedulerImpl{ jobs: make(map[int64]*AlertJob, 0), + log: log.New("alerting.scheduler"), } } func (s *SchedulerImpl) Update(rules []*AlertRule) { - log.Debug("Scheduler: Update()") + s.log.Debug("Scheduler: Update") jobs := make(map[int64]*AlertJob, 0) @@ -38,7 +40,7 @@ func (s *SchedulerImpl) Update(rules []*AlertRule) { jobs[rule.Id] = job } - log.Debug("Scheduler: Selected %d jobs", len(jobs)) + s.log.Debug("Scheduler: Selected %d jobs", len(jobs)) s.jobs = jobs } diff --git a/pkg/services/sqlstore/alert_rule.go b/pkg/services/sqlstore/alert_rule.go index 1228e4cfbff..bff0a36fc4c 100644 --- a/pkg/services/sqlstore/alert_rule.go +++ b/pkg/services/sqlstore/alert_rule.go @@ -64,7 +64,7 @@ func HeartBeat(query *m.HeartBeatCommand) error { */ func GetAlertById(query *m.GetAlertByIdQuery) error { - alert := m.AlertRule{} + alert := m.AlertRuleDAO{} has, err := x.Id(query.Id).Get(&alert) if !has { return fmt.Errorf("could not find alert") @@ -78,7 +78,7 @@ func GetAlertById(query *m.GetAlertByIdQuery) error { } func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error { - var alerts []*m.AlertRule + var alerts []*m.AlertRuleDAO err := x.Sql("select * from alert_rule").Find(&alerts) if err != nil { return err @@ -131,7 +131,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error { sql.WriteString(")") } - alerts := make([]*m.AlertRule, 0) + alerts := make([]*m.AlertRuleDAO, 0) if err := x.Sql(sql.String(), params...).Find(&alerts); err != nil { return err } @@ -141,7 +141,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error { } func DeleteAlertDefinition(dashboardId int64, sess *xorm.Session) error { - alerts := make([]*m.AlertRule, 0) + alerts := make([]*m.AlertRuleDAO, 0) sess.Where("dashboard_id = ?", dashboardId).Find(&alerts) for _, alert := range alerts { @@ -172,10 +172,10 @@ func SaveAlerts(cmd *m.SaveAlertsCommand) error { }) } -func upsertAlerts(alerts []*m.AlertRule, posted []*m.AlertRule, sess *xorm.Session) error { +func upsertAlerts(alerts []*m.AlertRuleDAO, posted []*m.AlertRuleDAO, sess *xorm.Session) error { for _, alert := range posted { update := false - var alertToUpdate *m.AlertRule + var alertToUpdate *m.AlertRuleDAO for _, k := range alerts { if alert.PanelId == k.PanelId { @@ -212,7 +212,7 @@ func upsertAlerts(alerts []*m.AlertRule, posted []*m.AlertRule, sess *xorm.Sessi return nil } -func deleteMissingAlerts(alerts []*m.AlertRule, posted []*m.AlertRule, sess *xorm.Session) error { +func deleteMissingAlerts(alerts []*m.AlertRuleDAO, posted []*m.AlertRuleDAO, sess *xorm.Session) error { for _, missingAlert := range alerts { missing := true @@ -238,12 +238,12 @@ func deleteMissingAlerts(alerts []*m.AlertRule, posted []*m.AlertRule, sess *xor return nil } -func GetAlertsByDashboardId2(dashboardId int64, sess *xorm.Session) ([]*m.AlertRule, error) { - alerts := make([]*m.AlertRule, 0) +func GetAlertsByDashboardId2(dashboardId int64, sess *xorm.Session) ([]*m.AlertRuleDAO, error) { + alerts := make([]*m.AlertRuleDAO, 0) err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts) if err != nil { - return []*m.AlertRule{}, err + return []*m.AlertRuleDAO{}, err } return alerts, nil diff --git a/pkg/services/sqlstore/alert_rule_changes.go b/pkg/services/sqlstore/alert_rule_changes.go index aa03e8e607d..b07e7ebfda6 100644 --- a/pkg/services/sqlstore/alert_rule_changes.go +++ b/pkg/services/sqlstore/alert_rule_changes.go @@ -48,7 +48,7 @@ func GetAlertRuleChanges(query *m.GetAlertChangesQuery) error { return nil } -func SaveAlertChange(change string, alert *m.AlertRule, sess *xorm.Session) error { +func SaveAlertChange(change string, alert *m.AlertRuleDAO, sess *xorm.Session) error { _, err := sess.Insert(&m.AlertRuleChange{ OrgId: alert.OrgId, Type: change, diff --git a/pkg/services/sqlstore/alert_rule_changes_test.go b/pkg/services/sqlstore/alert_rule_changes_test.go index cbd4ce93448..6b0a8785dab 100644 --- a/pkg/services/sqlstore/alert_rule_changes_test.go +++ b/pkg/services/sqlstore/alert_rule_changes_test.go @@ -20,7 +20,7 @@ func TestAlertRuleChangesDataAccess(t *testing.T) { var err error Convey("When dashboard is removed", func() { - items := []*m.AlertRule{ + items := []*m.AlertRuleDAO{ { PanelId: 1, DashboardId: testDash.Id, diff --git a/pkg/services/sqlstore/alert_rule_parser_test.go b/pkg/services/sqlstore/alert_rule_parser_test.go new file mode 100644 index 00000000000..03dec377710 --- /dev/null +++ b/pkg/services/sqlstore/alert_rule_parser_test.go @@ -0,0 +1,76 @@ +package sqlstore + +import ( + "testing" + + "github.com/grafana/grafana/pkg/components/simplejson" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/alerting" + . "github.com/smartystreets/goconvey/convey" +) + +func TestAlertRuleModelParsing(t *testing.T) { + + Convey("Parsing alertRule from expression", t, func() { + alertRuleDAO := &m.AlertRuleDAO{} + json, _ := simplejson.NewJson([]byte(` + { + "critical": { + "level": 20, + "op": ">" + }, + "description": "Alerting Panel Title alert", + "evalQuery": { + "agg": "avg", + "from": "5m", + "params": [ + "#A", + "5m", + "now", + "avg" + ], + "queryRefId": "A", + "to": "now" + }, + "evalStringParam1": "", + "frequency": 10, + "function": "static", + "name": "Alerting Panel Title alert", + "queryRef": "- select query -", + "valueQuery": { + "agg": "avg", + "datasourceId": 1, + "from": "5m", + "params": [ + "#A", + "5m", + "now", + "avg" + ], + "query": "aliasByNode(statsd.fakesite.counters.session_start.*.count, 4)", + "queryRefId": "A", + "to": "now" + }, + "warning": { + "level": 10, + "op": ">" + } + }`)) + + alertRuleDAO.Name = "Test" + alertRuleDAO.Expression = json + rule, _ := alerting.ParseAlertRulesFromAlertModel(alertRuleDAO) + + Convey("Confirm that all properties are set", func() { + So(rule.ValueQuery.Query, ShouldEqual, "aliasByNode(statsd.fakesite.counters.session_start.*.count, 4)") + So(rule.ValueQuery.From, ShouldEqual, "5m") + So(rule.ValueQuery.To, ShouldEqual, "now") + So(rule.ValueQuery.DatasourceId, ShouldEqual, 1) + So(rule.ValueQuery.Aggregator, ShouldEqual, "avg") + So(rule.Warning.Level, ShouldEqual, 10) + So(rule.Warning.Operator, ShouldEqual, ">") + So(rule.Critical.Level, ShouldEqual, 20) + So(rule.Critical.Operator, ShouldEqual, ">") + }) + }) +} diff --git a/pkg/services/sqlstore/alert_rule_test.go b/pkg/services/sqlstore/alert_rule_test.go index 9edcf20882a..939decd8e2d 100644 --- a/pkg/services/sqlstore/alert_rule_test.go +++ b/pkg/services/sqlstore/alert_rule_test.go @@ -13,7 +13,7 @@ func TestAlertingDataAccess(t *testing.T) { testDash := insertTestDashboard("dashboard with alerts", 1, "alert") - items := []*m.AlertRule{ + items := []*m.AlertRuleDAO{ { PanelId: 1, DashboardId: testDash.Id, @@ -95,7 +95,7 @@ func TestAlertingDataAccess(t *testing.T) { }) Convey("Multiple alerts per dashboard", func() { - multipleItems := []*m.AlertRule{ + multipleItems := []*m.AlertRuleDAO{ { DashboardId: testDash.Id, PanelId: 1, @@ -157,7 +157,7 @@ func TestAlertingDataAccess(t *testing.T) { }) Convey("When dashboard is removed", func() { - items := []*m.AlertRule{ + items := []*m.AlertRuleDAO{ { PanelId: 1, DashboardId: testDash.Id, diff --git a/pkg/services/sqlstore/alert_state.go b/pkg/services/sqlstore/alert_state.go index d2f3d4a4265..207e2f20385 100644 --- a/pkg/services/sqlstore/alert_state.go +++ b/pkg/services/sqlstore/alert_state.go @@ -19,7 +19,7 @@ func SetNewAlertState(cmd *m.UpdateAlertStateCommand) error { return fmt.Errorf("new state is invalid") } - alert := m.AlertRule{} + alert := m.AlertRuleDAO{} has, err := sess.Id(cmd.AlertId).Get(&alert) if !has { return fmt.Errorf("Could not find alert") diff --git a/pkg/services/sqlstore/alert_state_test.go b/pkg/services/sqlstore/alert_state_test.go index c112db9725d..7820b06525f 100644 --- a/pkg/services/sqlstore/alert_state_test.go +++ b/pkg/services/sqlstore/alert_state_test.go @@ -13,7 +13,7 @@ func TestAlertingStateAccess(t *testing.T) { testDash := insertTestDashboard("dashboard with alerts", 1, "alert") - items := []*m.AlertRule{ + items := []*m.AlertRuleDAO{ { PanelId: 1, DashboardId: testDash.Id, diff --git a/pkg/services/sqlstore/dashboard_parser_test.go b/pkg/services/sqlstore/dashboard_parser_test.go index 315ee203576..d8dc22b68c6 100644 --- a/pkg/services/sqlstore/dashboard_parser_test.go +++ b/pkg/services/sqlstore/dashboard_parser_test.go @@ -9,34 +9,25 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestAlertModel(t *testing.T) { +func TestAlertModelParsing(t *testing.T) { - Convey("Parsing alerts from dashboard", t, func() { - json := `{ + Convey("Parsing alert info from json", t, func() { + Convey("Parsing and validating alerts from dashboards", func() { + json := `{ "id": 57, "title": "Graphite 4", "originalTitle": "Graphite 4", "tags": [ "graphite" ], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "sharedCrosshair": false, "rows": [ { - "collapse": false, - "editable": true, - "height": "250px", + "panels": [ { "title": "Active desktop users", - "error": false, - "span": 6, "editable": true, "type": "graph", - "isNew": true, "id": 3, "targets": [ { @@ -45,65 +36,9 @@ func TestAlertModel(t *testing.T) { } ], "datasource": null, - "renderer": "flot", - "yaxes": [ - { - "label": null, - "show": true, - "logBase": 1, - "min": null, - "max": null, - "format": "short" - }, - { - "label": null, - "show": true, - "logBase": 1, - "min": null, - "max": null, - "format": "short" - } - ], - "xaxis": { - "show": true - }, - "grid": { - "threshold1": null, - "threshold2": null, - "threshold1Color": "rgba(216, 200, 27, 0.27)", - "threshold2Color": "rgba(234, 112, 112, 0.22)" - }, - "lines": true, - "fill": 1, - "linewidth": 2, - "points": false, - "pointradius": 5, - "bars": false, - "stack": false, - "percentage": false, - "legend": { - "show": true, - "values": false, - "min": false, - "max": false, - "current": false, - "total": false, - "avg": false - }, - "nullPointMode": "connected", - "steppedLine": false, - "tooltip": { - "value_type": "cumulative", - "shared": true, - "msResolution": false - }, - "timeFrom": null, - "timeShift": null, - "aliasColors": {}, - "seriesOverrides": [], - - "alerting": { + "name": "alert name", + "description": "description", "frequency": 10, "warning": { "op": ">", @@ -140,16 +75,10 @@ func TestAlertModel(t *testing.T) { }, "evalStringParam1": "", "name": "Alerting Panel Title alert" - }, - "links": [] + } }, { "title": "Active mobile users", - "error": false, - "span": 6, - "editable": true, - "type": "graph", - "isNew": true, "id": 4, "targets": [ { @@ -158,65 +87,9 @@ func TestAlertModel(t *testing.T) { } ], "datasource": "graphite2", - "renderer": "flot", - "yaxes": [ - { - "label": null, - "show": true, - "logBase": 1, - "min": null, - "max": null, - "format": "short" - }, - { - "label": null, - "show": true, - "logBase": 1, - "min": null, - "max": null, - "format": "short" - } - ], - "xaxis": { - "show": true - }, - "grid": { - "threshold1": null, - "threshold2": null, - "threshold1Color": "rgba(216, 200, 27, 0.27)", - "threshold2Color": "rgba(234, 112, 112, 0.22)" - }, - "lines": true, - "fill": 1, - "linewidth": 2, - "points": false, - "pointradius": 5, - "bars": false, - "stack": false, - "percentage": false, - "legend": { - "show": true, - "values": false, - "min": false, - "max": false, - "current": false, - "total": false, - "avg": false - }, - "nullPointMode": "connected", - "steppedLine": false, - "tooltip": { - "value_type": "cumulative", - "shared": true, - "msResolution": false - }, - "timeFrom": null, - "timeShift": null, - "aliasColors": { - "mobile": "#EAB839" - }, - "seriesOverrides": [], "alerting": { + "name": "alert name", + "description": "description", "frequency": 10, "warning": { "op": ">", @@ -253,8 +126,7 @@ func TestAlertModel(t *testing.T) { }, "evalStringParam1": "", "name": "Alerting Panel Title alert" - }, - "links": [] + } } ], "title": "Row" @@ -265,41 +137,8 @@ func TestAlertModel(t *testing.T) { "height": "250px", "panels": [ { - "columns": [], "datasource": "InfluxDB", - "editable": true, - "error": false, - "fontSize": "100%", "id": 2, - "isNew": true, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "span": 6, - "styles": [ - { - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], "targets": [ { "dsType": "influxdb", @@ -342,104 +181,60 @@ func TestAlertModel(t *testing.T) { ], "title": "Broken influxdb panel", "transform": "table", - "type": "table", - "links": [] + "type": "table" } ], "title": "New row" } - ], - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": { - "now": true, - "nowDelay": "5m", - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d", - "7d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "schemaVersion": 12, - "version": 16, - "links": [] + ] + }` - dashboardJson, _ := simplejson.NewJson([]byte(json)) - cmd := &m.SaveDashboardCommand{ - Dashboard: dashboardJson, - UserId: 1, - OrgId: 1, - Overwrite: true, - Result: &m.Dashboard{ - Id: 1, - }, - } - - InitTestDB(t) - - AddDataSource(&m.AddDataSourceCommand{ - Name: "graphite2", - OrgId: 1, - Type: m.DS_INFLUXDB, - Access: m.DS_ACCESS_DIRECT, - Url: "http://test", - IsDefault: false, - Database: "site", - }) - - AddDataSource(&m.AddDataSourceCommand{ - Name: "InfluxDB", - OrgId: 1, - Type: m.DS_GRAPHITE, - Access: m.DS_ACCESS_DIRECT, - Url: "http://test", - IsDefault: true, - }) - - alerts := alerting.ParseAlertsFromDashboard(cmd) - - Convey("all properties have been set", func() { - So(alerts, ShouldNotBeEmpty) - So(len(alerts), ShouldEqual, 2) - - for _, v := range alerts { - So(v.DashboardId, ShouldEqual, 1) - So(v.PanelId, ShouldNotEqual, 0) - - So(v.Name, ShouldNotBeEmpty) - So(v.Description, ShouldNotBeEmpty) - - expr := simplejson.NewFromAny(v.Expression) - So(expr.Get("valueQuery").Get("query").MustString(), ShouldNotEqual, "") - So(expr.Get("valueQuery").Get("datsourceId").MustInt64(), ShouldNotEqual, 0) + dashboardJSON, _ := simplejson.NewJson([]byte(json)) + cmd := &m.SaveDashboardCommand{ + Dashboard: dashboardJSON, + UserId: 1, + OrgId: 1, + Overwrite: true, + Result: &m.Dashboard{ + Id: 1, + }, } + + InitTestDB(t) + + AddDataSource(&m.AddDataSourceCommand{ + Name: "graphite2", + OrgId: 1, + Type: m.DS_INFLUXDB, + Access: m.DS_ACCESS_DIRECT, + Url: "http://test", + IsDefault: false, + Database: "site", + }) + + AddDataSource(&m.AddDataSourceCommand{ + Name: "InfluxDB", + OrgId: 1, + Type: m.DS_GRAPHITE, + Access: m.DS_ACCESS_DIRECT, + Url: "http://test", + IsDefault: true, + }) + + alerts := alerting.ParseAlertsFromDashboard(cmd) + + Convey("all properties have been set", func() { + So(alerts, ShouldNotBeEmpty) + So(len(alerts), ShouldEqual, 2) + + for _, v := range alerts { + So(v.DashboardId, ShouldEqual, 1) + So(v.PanelId, ShouldNotEqual, 0) + + So(v.Name, ShouldNotBeEmpty) + So(v.Description, ShouldNotBeEmpty) + } + }) }) }) } diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 36e03288879..0be6cf04e12 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -59,7 +59,7 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC var data []TargetResponseDTO err = json.Unmarshal(body, &data) if err != nil { - glog.Info("Failed to unmarshal graphite response", "error", err) + glog.Info("Failed to unmarshal graphite response", "error", err, "body", string(body)) result.Error = err return result } diff --git a/public/app/plugins/panel/graph/alert_tab_ctrl.ts b/public/app/plugins/panel/graph/alert_tab_ctrl.ts index 1b1f052632c..0f5f8b58be3 100644 --- a/public/app/plugins/panel/graph/alert_tab_ctrl.ts +++ b/public/app/plugins/panel/graph/alert_tab_ctrl.ts @@ -54,13 +54,13 @@ export class AlertTabCtrl { function: 'static', valueQuery: { queryRefId: 'A', - from: '5m', + from: '600s', to: 'now', agg: 'avg', }, evalQuery: { queryRefId: 'A', - from: '5m', + from: '600s', to: 'now', agg: 'avg', },