rule.go 5.0 KB

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