evaluator.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package conditions
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/grafana/grafana/pkg/components/null"
  6. "github.com/grafana/grafana/pkg/components/simplejson"
  7. "github.com/grafana/grafana/pkg/services/alerting"
  8. )
  9. var (
  10. defaultTypes = []string{"gt", "lt"}
  11. rangedTypes = []string{"within_range", "outside_range"}
  12. )
  13. // AlertEvaluator evaluates the reduced value of a timeserie.
  14. // Returning true if a timeserie is violating the condition
  15. // ex: ThresholdEvaluator, NoValueEvaluator, RangeEvaluator
  16. type AlertEvaluator interface {
  17. Eval(reducedValue null.Float) bool
  18. }
  19. type noValueEvaluator struct{}
  20. func (e *noValueEvaluator) Eval(reducedValue null.Float) bool {
  21. return !reducedValue.Valid
  22. }
  23. type thresholdEvaluator struct {
  24. Type string
  25. Threshold float64
  26. }
  27. func newThresholdEvaluator(typ string, model *simplejson.Json) (*thresholdEvaluator, error) {
  28. params := model.Get("params").MustArray()
  29. if len(params) == 0 || params[0] == nil {
  30. return nil, fmt.Errorf("Evaluator '%v' is missing the threshold parameter", HumanThresholdType(typ))
  31. }
  32. firstParam, ok := params[0].(json.Number)
  33. if !ok {
  34. return nil, fmt.Errorf("Evaluator has invalid parameter")
  35. }
  36. defaultEval := &thresholdEvaluator{Type: typ}
  37. defaultEval.Threshold, _ = firstParam.Float64()
  38. return defaultEval, nil
  39. }
  40. func (e *thresholdEvaluator) Eval(reducedValue null.Float) bool {
  41. if !reducedValue.Valid {
  42. return false
  43. }
  44. switch e.Type {
  45. case "gt":
  46. return reducedValue.Float64 > e.Threshold
  47. case "lt":
  48. return reducedValue.Float64 < e.Threshold
  49. }
  50. return false
  51. }
  52. type rangedEvaluator struct {
  53. Type string
  54. Lower float64
  55. Upper float64
  56. }
  57. func newRangedEvaluator(typ string, model *simplejson.Json) (*rangedEvaluator, error) {
  58. params := model.Get("params").MustArray()
  59. if len(params) == 0 {
  60. return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
  61. }
  62. firstParam, ok := params[0].(json.Number)
  63. if !ok {
  64. return nil, alerting.ValidationError{Reason: "Evaluator has invalid parameter"}
  65. }
  66. secondParam, ok := params[1].(json.Number)
  67. if !ok {
  68. return nil, alerting.ValidationError{Reason: "Evaluator has invalid second parameter"}
  69. }
  70. rangedEval := &rangedEvaluator{Type: typ}
  71. rangedEval.Lower, _ = firstParam.Float64()
  72. rangedEval.Upper, _ = secondParam.Float64()
  73. return rangedEval, nil
  74. }
  75. func (e *rangedEvaluator) Eval(reducedValue null.Float) bool {
  76. if !reducedValue.Valid {
  77. return false
  78. }
  79. floatValue := reducedValue.Float64
  80. switch e.Type {
  81. case "within_range":
  82. return (e.Lower < floatValue && e.Upper > floatValue) || (e.Upper < floatValue && e.Lower > floatValue)
  83. case "outside_range":
  84. return (e.Upper < floatValue && e.Lower < floatValue) || (e.Upper > floatValue && e.Lower > floatValue)
  85. }
  86. return false
  87. }
  88. // NewAlertEvaluator is a factory function for returning
  89. // an `AlertEvaluator` depending on the json model.
  90. func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) {
  91. typ := model.Get("type").MustString()
  92. if typ == "" {
  93. return nil, fmt.Errorf("Evaluator missing type property")
  94. }
  95. if inSlice(typ, defaultTypes) {
  96. return newThresholdEvaluator(typ, model)
  97. }
  98. if inSlice(typ, rangedTypes) {
  99. return newRangedEvaluator(typ, model)
  100. }
  101. if typ == "no_value" {
  102. return &noValueEvaluator{}, nil
  103. }
  104. return nil, fmt.Errorf("Evaluator invalid evaluator type: %s", typ)
  105. }
  106. func inSlice(a string, list []string) bool {
  107. for _, b := range list {
  108. if b == a {
  109. return true
  110. }
  111. }
  112. return false
  113. }
  114. // HumanThresholdType converts a treshold "type" string to a string that matches the UI
  115. // so errors are less confusing.
  116. func HumanThresholdType(typ string) string {
  117. switch typ {
  118. case "gt":
  119. return "IS ABOVE"
  120. case "lt":
  121. return "IS BELOW"
  122. case "within_range":
  123. return "IS WITHIN RANGE"
  124. case "outside_range":
  125. return "IS OUTSIDE RANGE"
  126. }
  127. return ""
  128. }