rule.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package alerting
  2. import (
  3. "errors"
  4. "fmt"
  5. "regexp"
  6. "strconv"
  7. "time"
  8. "github.com/grafana/grafana/pkg/components/simplejson"
  9. m "github.com/grafana/grafana/pkg/models"
  10. )
  11. var (
  12. ErrFrequencyCannotBeZeroOrLess = errors.New(`"evaluate every" cannot be zero or below`)
  13. ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`)
  14. )
  15. type Rule struct {
  16. Id int64
  17. OrgId int64
  18. DashboardId int64
  19. PanelId int64
  20. Frequency int64
  21. Name string
  22. Message string
  23. LastStateChange time.Time
  24. For time.Duration
  25. NoDataState m.NoDataOption
  26. ExecutionErrorState m.ExecutionErrorOption
  27. State m.AlertStateType
  28. Conditions []Condition
  29. Notifications []int64
  30. StateChanges int64
  31. }
  32. type ValidationError struct {
  33. Reason string
  34. Err error
  35. Alertid int64
  36. DashboardId int64
  37. PanelId int64
  38. }
  39. func (e ValidationError) Error() string {
  40. extraInfo := e.Reason
  41. if e.Alertid != 0 {
  42. extraInfo = fmt.Sprintf("%s AlertId: %v", extraInfo, e.Alertid)
  43. }
  44. if e.PanelId != 0 {
  45. extraInfo = fmt.Sprintf("%s PanelId: %v", extraInfo, e.PanelId)
  46. }
  47. if e.DashboardId != 0 {
  48. extraInfo = fmt.Sprintf("%s DashboardId: %v", extraInfo, e.DashboardId)
  49. }
  50. if e.Err != nil {
  51. return fmt.Sprintf("Alert validation error: %s%s", e.Err.Error(), extraInfo)
  52. }
  53. return fmt.Sprintf("Alert validation error: %s", extraInfo)
  54. }
  55. var (
  56. ValueFormatRegex = regexp.MustCompile(`^\d+`)
  57. UnitFormatRegex = regexp.MustCompile(`\w{1}$`)
  58. )
  59. var unitMultiplier = map[string]int{
  60. "s": 1,
  61. "m": 60,
  62. "h": 3600,
  63. }
  64. func getTimeDurationStringToSeconds(str string) (int64, error) {
  65. multiplier := 1
  66. matches := ValueFormatRegex.FindAllString(str, 1)
  67. if len(matches) <= 0 {
  68. return 0, ErrFrequencyCouldNotBeParsed
  69. }
  70. value, err := strconv.Atoi(matches[0])
  71. if err != nil {
  72. return 0, err
  73. }
  74. if value == 0 {
  75. return 0, ErrFrequencyCannotBeZeroOrLess
  76. }
  77. unit := UnitFormatRegex.FindAllString(str, 1)[0]
  78. if val, ok := unitMultiplier[unit]; ok {
  79. multiplier = val
  80. }
  81. return int64(value * multiplier), nil
  82. }
  83. func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
  84. model := &Rule{}
  85. model.Id = ruleDef.Id
  86. model.OrgId = ruleDef.OrgId
  87. model.DashboardId = ruleDef.DashboardId
  88. model.PanelId = ruleDef.PanelId
  89. model.Name = ruleDef.Name
  90. model.Message = ruleDef.Message
  91. model.State = ruleDef.State
  92. model.LastStateChange = ruleDef.NewStateDate
  93. model.For = ruleDef.For
  94. model.NoDataState = m.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
  95. model.ExecutionErrorState = m.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting"))
  96. model.StateChanges = ruleDef.StateChanges
  97. model.Frequency = ruleDef.Frequency
  98. // frequency cannot be zero since that would not execute the alert rule.
  99. // so we fallback to 60 seconds if `Freqency` is missing
  100. if model.Frequency == 0 {
  101. model.Frequency = 60
  102. }
  103. for _, v := range ruleDef.Settings.Get("notifications").MustArray() {
  104. jsonModel := simplejson.NewFromAny(v)
  105. id, err := jsonModel.Get("id").Int64()
  106. if err != nil {
  107. return nil, ValidationError{Reason: "Invalid notification schema", DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  108. }
  109. model.Notifications = append(model.Notifications, id)
  110. }
  111. for index, condition := range ruleDef.Settings.Get("conditions").MustArray() {
  112. conditionModel := simplejson.NewFromAny(condition)
  113. conditionType := conditionModel.Get("type").MustString()
  114. factory, exist := conditionFactories[conditionType]
  115. if !exist {
  116. return nil, ValidationError{Reason: "Unknown alert condition: " + conditionType, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  117. }
  118. queryCondition, err := factory(conditionModel, index)
  119. if err != nil {
  120. return nil, ValidationError{Err: err, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  121. }
  122. model.Conditions = append(model.Conditions, queryCondition)
  123. }
  124. if len(model.Conditions) == 0 {
  125. return nil, ValidationError{Reason: "Alert is missing conditions"}
  126. }
  127. return model, nil
  128. }
  129. type ConditionFactory func(model *simplejson.Json, index int) (Condition, error)
  130. var conditionFactories = make(map[string]ConditionFactory)
  131. func RegisterCondition(typeName string, factory ConditionFactory) {
  132. conditionFactories[typeName] = factory
  133. }