rule.go 5.1 KB

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