rule.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 []string
  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. "d": 86400,
  64. }
  65. func getTimeDurationStringToSeconds(str string) (int64, error) {
  66. multiplier := 1
  67. matches := ValueFormatRegex.FindAllString(str, 1)
  68. if len(matches) <= 0 {
  69. return 0, ErrFrequencyCouldNotBeParsed
  70. }
  71. value, err := strconv.Atoi(matches[0])
  72. if err != nil {
  73. return 0, err
  74. }
  75. if value == 0 {
  76. return 0, ErrFrequencyCannotBeZeroOrLess
  77. }
  78. unit := UnitFormatRegex.FindAllString(str, 1)[0]
  79. if val, ok := unitMultiplier[unit]; ok {
  80. multiplier = val
  81. }
  82. return int64(value * multiplier), nil
  83. }
  84. func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
  85. model := &Rule{}
  86. model.Id = ruleDef.Id
  87. model.OrgId = ruleDef.OrgId
  88. model.DashboardId = ruleDef.DashboardId
  89. model.PanelId = ruleDef.PanelId
  90. model.Name = ruleDef.Name
  91. model.Message = ruleDef.Message
  92. model.State = ruleDef.State
  93. model.LastStateChange = ruleDef.NewStateDate
  94. model.For = ruleDef.For
  95. model.NoDataState = m.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
  96. model.ExecutionErrorState = m.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting"))
  97. model.StateChanges = ruleDef.StateChanges
  98. model.Frequency = ruleDef.Frequency
  99. // frequency cannot be zero since that would not execute the alert rule.
  100. // so we fallback to 60 seconds if `Freqency` is missing
  101. if model.Frequency == 0 {
  102. model.Frequency = 60
  103. }
  104. for _, v := range ruleDef.Settings.Get("notifications").MustArray() {
  105. jsonModel := simplejson.NewFromAny(v)
  106. if id, err := jsonModel.Get("id").Int64(); err == nil {
  107. model.Notifications = append(model.Notifications, fmt.Sprintf("%09d", id))
  108. } else {
  109. uid, err := jsonModel.Get("uid").String()
  110. if err != nil {
  111. return nil, ValidationError{Reason: "Neither id nor uid is specified, " + err.Error(), DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  112. }
  113. model.Notifications = append(model.Notifications, uid)
  114. }
  115. }
  116. for index, condition := range ruleDef.Settings.Get("conditions").MustArray() {
  117. conditionModel := simplejson.NewFromAny(condition)
  118. conditionType := conditionModel.Get("type").MustString()
  119. factory, exist := conditionFactories[conditionType]
  120. if !exist {
  121. return nil, ValidationError{Reason: "Unknown alert condition: " + conditionType, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  122. }
  123. queryCondition, err := factory(conditionModel, index)
  124. if err != nil {
  125. return nil, ValidationError{Err: err, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId}
  126. }
  127. model.Conditions = append(model.Conditions, queryCondition)
  128. }
  129. if len(model.Conditions) == 0 {
  130. return nil, ValidationError{Reason: "Alert is missing conditions"}
  131. }
  132. return model, nil
  133. }
  134. type ConditionFactory func(model *simplejson.Json, index int) (Condition, error)
  135. var conditionFactories = make(map[string]ConditionFactory)
  136. func RegisterCondition(typeName string, factory ConditionFactory) {
  137. conditionFactories[typeName] = factory
  138. }