2016-07-27 09:18:10 -05:00
|
|
|
package conditions
|
2016-07-19 09:15:26 -05:00
|
|
|
|
2016-07-19 10:45:37 -05:00
|
|
|
import (
|
2016-07-20 07:28:02 -05:00
|
|
|
"fmt"
|
2016-09-21 00:01:53 -05:00
|
|
|
"strings"
|
|
|
|
"time"
|
2016-07-19 09:15:26 -05:00
|
|
|
|
2016-07-20 07:28:02 -05:00
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
2016-07-19 10:45:37 -05:00
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
2016-07-20 07:28:02 -05:00
|
|
|
m "github.com/grafana/grafana/pkg/models"
|
2016-07-27 09:18:10 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/alerting"
|
2016-07-20 07:28:02 -05:00
|
|
|
"github.com/grafana/grafana/pkg/tsdb"
|
2016-07-19 10:45:37 -05:00
|
|
|
)
|
2016-07-19 09:15:26 -05:00
|
|
|
|
2016-07-27 09:18:10 -05:00
|
|
|
func init() {
|
2016-07-27 09:29:28 -05:00
|
|
|
alerting.RegisterCondition("query", func(model *simplejson.Json, index int) (alerting.Condition, error) {
|
2016-07-27 09:18:10 -05:00
|
|
|
return NewQueryCondition(model, index)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-07-19 09:15:26 -05:00
|
|
|
type QueryCondition struct {
|
2016-07-21 14:54:12 -05:00
|
|
|
Index int
|
2016-07-20 07:28:02 -05:00
|
|
|
Query AlertQuery
|
|
|
|
Reducer QueryReducer
|
|
|
|
Evaluator AlertEvaluator
|
|
|
|
HandleRequest tsdb.HandleRequestFunc
|
2016-07-19 09:15:26 -05:00
|
|
|
}
|
|
|
|
|
2016-07-27 09:18:10 -05:00
|
|
|
type AlertQuery struct {
|
|
|
|
Model *simplejson.Json
|
|
|
|
DatasourceId int64
|
|
|
|
From string
|
|
|
|
To string
|
|
|
|
}
|
|
|
|
|
2016-07-27 09:29:28 -05:00
|
|
|
func (c *QueryCondition) Eval(context *alerting.EvalContext) {
|
2016-09-27 07:39:51 -05:00
|
|
|
timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To)
|
|
|
|
seriesList, err := c.executeQuery(context, timeRange)
|
2016-07-20 07:28:02 -05:00
|
|
|
if err != nil {
|
|
|
|
context.Error = err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-09-08 07:33:10 -05:00
|
|
|
emptySerieCount := 0
|
2016-07-20 07:28:02 -05:00
|
|
|
for _, series := range seriesList {
|
|
|
|
reducedValue := c.Reducer.Reduce(series)
|
2016-09-06 13:40:12 -05:00
|
|
|
evalMatch := c.Evaluator.Eval(reducedValue)
|
2016-07-21 14:54:12 -05:00
|
|
|
|
2016-09-28 02:15:48 -05:00
|
|
|
if reducedValue.Valid == false {
|
2016-09-16 07:58:10 -05:00
|
|
|
emptySerieCount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-07-21 14:54:12 -05:00
|
|
|
if context.IsTestRun {
|
2016-07-27 09:29:28 -05:00
|
|
|
context.Logs = append(context.Logs, &alerting.ResultLogEntry{
|
2016-09-28 02:15:48 -05:00
|
|
|
Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, evalMatch, series.Name, reducedValue.Float64),
|
2016-07-21 14:54:12 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-08-15 08:12:43 -05:00
|
|
|
if evalMatch {
|
|
|
|
context.EvalMatches = append(context.EvalMatches, &alerting.EvalMatch{
|
2016-07-21 14:54:12 -05:00
|
|
|
Metric: series.Name,
|
2016-09-28 02:15:48 -05:00
|
|
|
Value: reducedValue.Float64,
|
2016-07-21 14:54:12 -05:00
|
|
|
})
|
2016-07-20 07:28:02 -05:00
|
|
|
}
|
|
|
|
}
|
2016-09-08 06:28:41 -05:00
|
|
|
|
2016-09-08 07:33:10 -05:00
|
|
|
context.NoDataFound = emptySerieCount == len(seriesList)
|
2016-09-08 06:28:41 -05:00
|
|
|
context.Firing = len(context.EvalMatches) > 0
|
2016-07-20 07:28:02 -05:00
|
|
|
}
|
|
|
|
|
2016-09-27 11:17:39 -05:00
|
|
|
func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) {
|
2016-07-20 07:28:02 -05:00
|
|
|
getDsInfo := &m.GetDataSourceByIdQuery{
|
|
|
|
Id: c.Query.DatasourceId,
|
|
|
|
OrgId: context.Rule.OrgId,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bus.Dispatch(getDsInfo); err != nil {
|
|
|
|
return nil, fmt.Errorf("Could not find datasource")
|
|
|
|
}
|
|
|
|
|
2016-09-27 07:39:51 -05:00
|
|
|
req := c.getRequestForAlertRule(getDsInfo.Result, timeRange)
|
2016-07-20 07:28:02 -05:00
|
|
|
result := make(tsdb.TimeSeriesSlice, 0)
|
|
|
|
|
2016-10-03 10:03:21 -05:00
|
|
|
resp, err := c.HandleRequest(context.Ctx, req)
|
2016-07-20 07:28:02 -05:00
|
|
|
if err != nil {
|
2016-07-21 06:09:12 -05:00
|
|
|
return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
|
2016-07-20 07:28:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range resp.Results {
|
|
|
|
if v.Error != nil {
|
2016-07-21 06:09:12 -05:00
|
|
|
return nil, fmt.Errorf("tsdb.HandleRequest() response error %v", v)
|
2016-07-20 07:28:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
result = append(result, v.Series...)
|
2016-07-21 06:09:12 -05:00
|
|
|
|
|
|
|
if context.IsTestRun {
|
2016-07-27 09:29:28 -05:00
|
|
|
context.Logs = append(context.Logs, &alerting.ResultLogEntry{
|
2016-07-21 14:54:12 -05:00
|
|
|
Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
|
2016-07-21 06:09:12 -05:00
|
|
|
Data: v.Series,
|
|
|
|
})
|
|
|
|
}
|
2016-07-20 07:28:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2016-09-27 11:17:39 -05:00
|
|
|
func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timeRange *tsdb.TimeRange) *tsdb.Request {
|
2016-07-20 07:28:02 -05:00
|
|
|
req := &tsdb.Request{
|
2016-09-27 11:17:39 -05:00
|
|
|
TimeRange: timeRange,
|
2016-07-20 07:28:02 -05:00
|
|
|
Queries: []*tsdb.Query{
|
|
|
|
{
|
|
|
|
RefId: "A",
|
2016-09-21 04:17:29 -05:00
|
|
|
Model: c.Query.Model,
|
2016-07-20 07:28:02 -05:00
|
|
|
DataSource: &tsdb.DataSourceInfo{
|
2016-08-29 08:49:25 -05:00
|
|
|
Id: datasource.Id,
|
|
|
|
Name: datasource.Name,
|
|
|
|
PluginId: datasource.Type,
|
|
|
|
Url: datasource.Url,
|
|
|
|
User: datasource.User,
|
|
|
|
Password: datasource.Password,
|
|
|
|
Database: datasource.Database,
|
|
|
|
BasicAuth: datasource.BasicAuth,
|
|
|
|
BasicAuthUser: datasource.BasicAuthUser,
|
|
|
|
BasicAuthPassword: datasource.BasicAuthPassword,
|
2016-10-03 07:31:33 -05:00
|
|
|
JsonData: datasource.JsonData,
|
2016-07-20 07:28:02 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return req
|
2016-07-19 09:15:26 -05:00
|
|
|
}
|
|
|
|
|
2016-07-21 14:54:12 -05:00
|
|
|
func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
|
2016-07-19 10:45:37 -05:00
|
|
|
condition := QueryCondition{}
|
2016-07-21 14:54:12 -05:00
|
|
|
condition.Index = index
|
2016-07-20 07:28:02 -05:00
|
|
|
condition.HandleRequest = tsdb.HandleRequest
|
2016-07-19 10:45:37 -05:00
|
|
|
|
|
|
|
queryJson := model.Get("query")
|
|
|
|
|
2016-07-20 07:28:02 -05:00
|
|
|
condition.Query.Model = queryJson.Get("model")
|
2016-07-19 10:45:37 -05:00
|
|
|
condition.Query.From = queryJson.Get("params").MustArray()[1].(string)
|
|
|
|
condition.Query.To = queryJson.Get("params").MustArray()[2].(string)
|
2016-09-21 00:01:53 -05:00
|
|
|
|
|
|
|
if err := validateFromValue(condition.Query.From); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := validateToValue(condition.Query.To); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-07-19 10:45:37 -05:00
|
|
|
condition.Query.DatasourceId = queryJson.Get("datasourceId").MustInt64()
|
|
|
|
|
|
|
|
reducerJson := model.Get("reducer")
|
|
|
|
condition.Reducer = NewSimpleReducer(reducerJson.Get("type").MustString())
|
|
|
|
|
|
|
|
evaluatorJson := model.Get("evaluator")
|
2016-08-16 05:34:54 -05:00
|
|
|
evaluator, err := NewAlertEvaluator(evaluatorJson)
|
2016-07-19 10:45:37 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
condition.Evaluator = evaluator
|
|
|
|
return &condition, nil
|
2016-07-19 09:15:26 -05:00
|
|
|
}
|
2016-09-21 00:01:53 -05:00
|
|
|
|
|
|
|
func validateFromValue(from string) error {
|
|
|
|
fromRaw := strings.Replace(from, "now-", "", 1)
|
|
|
|
|
|
|
|
_, err := time.ParseDuration("-" + fromRaw)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateToValue(to string) error {
|
|
|
|
if to == "now" {
|
|
|
|
return nil
|
|
|
|
} else if strings.HasPrefix(to, "now-") {
|
|
|
|
withoutNow := strings.Replace(to, "now-", "", 1)
|
|
|
|
|
|
|
|
_, err := time.ParseDuration("-" + withoutNow)
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-26 03:01:33 -05:00
|
|
|
_, err := time.ParseDuration(to)
|
|
|
|
return err
|
2016-09-21 00:01:53 -05:00
|
|
|
}
|