rule.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package alerting
  15. import (
  16. "context"
  17. "regexp"
  18. "strconv"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/onecloud/pkg/apis/monitor"
  23. "yunion.io/x/onecloud/pkg/mcclient"
  24. "yunion.io/x/onecloud/pkg/monitor/models"
  25. "yunion.io/x/onecloud/pkg/monitor/validators"
  26. )
  27. var (
  28. // ErrFrequencyCannotBeZeroOrLess frequency cannot be below zero
  29. ErrFrequencyCannotBeZeroOrLess = errors.Error(`"evaluate every" cannot be zero or below`)
  30. // ErrFrequencyCouldNotBeParsed frequency cannot be parsed
  31. ErrFrequencyCouldNotBeParsed = errors.Error(`"evaluate every" field could not be parsed`)
  32. )
  33. func init() {
  34. models.AlertManager.SetTester(NewAlertRuleTester())
  35. }
  36. // Rule is the in-memory version of an alert rule.
  37. type Rule struct {
  38. Id string
  39. Frequency int64
  40. Title string
  41. Name string
  42. Message string
  43. // 使用 TriggeredMessages 存储触发的条件,替代 Message
  44. TriggeredMessages []string
  45. LastStateChange time.Time
  46. For time.Duration
  47. NoDataState monitor.NoDataOption
  48. ExecutionErrorState monitor.ExecutionErrorOption
  49. State monitor.AlertStateType
  50. Conditions []Condition
  51. Notifications []string
  52. DisableNotifyRecovery bool
  53. // AlertRuleTags []*models.AlertRuleTag
  54. Level string
  55. Reason string
  56. RuleDescription []*monitor.AlertRecordRule
  57. StateChanges int
  58. CustomizeConfig jsonutils.JSONObject
  59. // 静默期
  60. SilentPeriod int64
  61. }
  62. var (
  63. valueFormatRegex = regexp.MustCompile(`^\d+`)
  64. unitFormatRegex = regexp.MustCompile(`\w{1}$`)
  65. )
  66. var unitMultiplier = map[string]int{
  67. "s": 1,
  68. "m": 60,
  69. "h": 3600,
  70. "d": 86400,
  71. }
  72. func getTimeDurationStringToSeconds(str string) (int64, error) {
  73. multiplier := 1
  74. matches := valueFormatRegex.FindAllString(str, 1)
  75. if len(matches) <= 0 {
  76. return 0, ErrFrequencyCouldNotBeParsed
  77. }
  78. value, err := strconv.Atoi(matches[0])
  79. if err != nil {
  80. return 0, err
  81. }
  82. if value == 0 {
  83. return 0, ErrFrequencyCannotBeZeroOrLess
  84. }
  85. unit := unitFormatRegex.FindAllString(str, 1)[0]
  86. if val, ok := unitMultiplier[unit]; ok {
  87. multiplier = val
  88. }
  89. return int64(value * multiplier), nil
  90. }
  91. // NewRuleFromDBAlert maps an db version of
  92. // alert to an in-memory version
  93. func NewRuleFromDBAlert(ruleDef *models.SAlert) (*Rule, error) {
  94. model := &Rule{}
  95. model.Id = ruleDef.Id
  96. model.Title = ruleDef.GetTitle()
  97. model.Name = ruleDef.Name
  98. model.Message = ruleDef.Message
  99. model.TriggeredMessages = make([]string, 0)
  100. model.State = monitor.AlertStateType(ruleDef.State)
  101. model.LastStateChange = ruleDef.LastStateChange
  102. model.For = time.Duration(ruleDef.For)
  103. model.NoDataState = monitor.NoDataOption(ruleDef.NoDataState)
  104. model.ExecutionErrorState = monitor.ExecutionErrorOption(ruleDef.ExecutionErrorState)
  105. model.StateChanges = ruleDef.StateChanges
  106. model.RuleDescription = make([]*monitor.AlertRecordRule, 0)
  107. model.Frequency = ruleDef.Frequency
  108. model.DisableNotifyRecovery = ruleDef.DisableNotifyRecovery
  109. // frequency cannot be zero since that would not execute the alert rule.
  110. // so we fallback to 60 seconds if `Frequency` is missing
  111. if model.Frequency == 0 {
  112. model.Frequency = 60
  113. }
  114. model.CustomizeConfig = ruleDef.CustomizeConfig
  115. settings, err := ruleDef.GetSettings()
  116. if err != nil {
  117. return nil, errors.Wrap(err, "get settings")
  118. }
  119. model.Level = ruleDef.Level
  120. model.Reason = ruleDef.Reason
  121. nIds := []string{}
  122. notis, err := ruleDef.GetNotifications()
  123. if err != nil {
  124. return nil, err
  125. }
  126. for _, n := range notis {
  127. noti, _ := n.GetNotification()
  128. if noti != nil && noti.Frequency != 0 {
  129. model.SilentPeriod = noti.Frequency
  130. }
  131. nIds = append(nIds, n.NotificationId)
  132. }
  133. model.Notifications = nIds
  134. // model.AlertRuleTags = ruleDef.GetTagsFromSettings()
  135. alert, err := models.CommonAlertManager.GetAlert(ruleDef.Id)
  136. if err != nil {
  137. return nil, errors.Wrap(err, "GetCommonAlert error")
  138. }
  139. for index, condition := range settings.Conditions {
  140. condType := condition.Type
  141. factory, exist := conditionFactories[condType]
  142. if !exist {
  143. return nil, errors.Wrapf(validators.ErrAlertConditionUnknown, "condition type %s", condType)
  144. }
  145. queryCond, err := factory(&condition, index)
  146. if err != nil {
  147. return nil, errors.Wrapf(err, "construct query condition %s", jsonutils.Marshal(condition))
  148. }
  149. ruleDesc := alert.GetAlertRule(settings, index, model.SilentPeriod)
  150. model.RuleDescription = append(model.RuleDescription, ruleDesc)
  151. model.Conditions = append(model.Conditions, queryCond)
  152. }
  153. if len(model.Conditions) == 0 {
  154. return nil, validators.ErrAlertConditionEmpty
  155. }
  156. return model, nil
  157. }
  158. type AlertRuleTester struct{}
  159. func NewAlertRuleTester() models.AlertTestRunner {
  160. return new(AlertRuleTester)
  161. }
  162. func (_ AlertRuleTester) DoTest(ruleDef *models.SAlert, userCred mcclient.TokenCredential, input monitor.AlertTestRunInput) (*monitor.AlertTestRunOutput, error) {
  163. rule, err := NewRuleFromDBAlert(ruleDef)
  164. if err != nil {
  165. return nil, err
  166. }
  167. handler := NewEvalHandler()
  168. ctx := NewEvalContext(context.Background(), userCred, rule)
  169. ctx.IsTestRun = true
  170. ctx.IsDebug = input.IsDebug
  171. handler.Eval(ctx)
  172. return ctx.ToTestRunResult(), nil
  173. }
  174. func (ctx *EvalContext) ToTestRunResult() *monitor.AlertTestRunOutput {
  175. return &monitor.AlertTestRunOutput{
  176. Firing: ctx.Firing,
  177. IsTestRun: ctx.IsTestRun,
  178. IsDebug: ctx.IsDebug,
  179. EvalMatches: ctx.EvalMatches,
  180. AlertOKEvalMatches: ctx.AlertOkEvalMatches,
  181. Logs: ctx.Logs,
  182. Error: ctx.Error,
  183. ConditionEvals: ctx.ConditionEvals,
  184. StartTime: ctx.StartTime,
  185. EndTime: ctx.EndTime,
  186. NoDataFound: ctx.NoDataFound,
  187. PrevAlertState: string(ctx.PrevAlertState),
  188. }
  189. }
  190. // ConditionFactory is the function signature for creating `Conditions`
  191. type ConditionFactory func(model *monitor.AlertCondition, index int) (Condition, error)
  192. var conditionFactories = make(map[string]ConditionFactory)
  193. // RegisterCondition adds support for alerting conditions.
  194. func RegisterCondition(typeName string, factory ConditionFactory) {
  195. conditionFactories[typeName] = factory
  196. }
  197. func GetConditionFactories() map[string]ConditionFactory {
  198. return conditionFactories
  199. }