validators.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 validators
  15. import (
  16. "strings"
  17. "time"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/pkg/utils"
  20. "yunion.io/x/onecloud/pkg/apis/monitor"
  21. "yunion.io/x/onecloud/pkg/httperrors"
  22. merrors "yunion.io/x/onecloud/pkg/monitor/errors"
  23. "yunion.io/x/onecloud/pkg/monitor/tsdb"
  24. )
  25. const (
  26. ErrMissingParameterThreshold = errors.Error("Condition is missing the threshold parameter")
  27. ErrMissingParameterType = errors.Error("Condition is missing the type parameter")
  28. ErrInvalidEvaluatorType = errors.Error("Invalid condition evaluator type")
  29. ErrAlertConditionUnknown = errors.Error("Unknown alert condition")
  30. ErrAlertConditionEmpty = errors.Error("Alert is missing conditions")
  31. )
  32. var (
  33. EvaluatorDefaultTypes = []string{string(monitor.EvaluatorTypeGT), string(monitor.EvaluatorTypeLT), string(monitor.EvaluatorTypeEQ)}
  34. EvaluatorRangedTypes = []string{string(monitor.EvaluatorTypeWithinRange), string(monitor.EvaluatorTypeOutsideRange)}
  35. CommonAlertType = []string{
  36. monitor.CommonAlertNomalAlertType,
  37. monitor.CommonAlertSystemAlertType,
  38. monitor.CommonAlertServiceAlertType,
  39. }
  40. CommonAlertReducerFieldOpts = []string{"/"}
  41. CommonAlertNotifyTypes = []string{"email", "mobile", "dingtalk", "webconsole", "feishu"}
  42. ConditionTypes = []string{"query", monitor.METRIC_QUERY_TYPE_NO_DATA}
  43. )
  44. func ValidateAlertCreateInput(input monitor.AlertCreateInput) error {
  45. if len(input.Settings.Conditions) == 0 {
  46. return httperrors.NewInputParameterError("input condition is empty")
  47. }
  48. for _, condition := range input.Settings.Conditions {
  49. if err := ValidateAlertCondition(condition); err != nil {
  50. return err
  51. }
  52. }
  53. return nil
  54. }
  55. func ValidateAlertCondition(input monitor.AlertCondition) error {
  56. condType := input.Type
  57. if err := ValidateAlertConditionType(condType); err != nil {
  58. return err
  59. }
  60. if err := ValidateAlertConditionQuery(input.Query); err != nil {
  61. return err
  62. }
  63. if err := ValidateAlertConditionReducer(input.Reducer); err != nil {
  64. return err
  65. }
  66. if err := ValidateAlertConditionEvaluator(input.Evaluator); err != nil {
  67. return err
  68. }
  69. if input.Operator == "" {
  70. input.Operator = "and"
  71. }
  72. if !utils.IsInStringArray(input.Operator, []string{"and", "or"}) {
  73. return httperrors.NewInputParameterError("Unkown operator %s", input.Operator)
  74. }
  75. return nil
  76. }
  77. func ValidateAlertConditionQuery(input monitor.AlertQuery) error {
  78. if err := ValidateFromValue(input.From); err != nil {
  79. return err
  80. }
  81. if err := ValidateToValue(input.To); err != nil {
  82. return err
  83. }
  84. if err := ValidateAlertQueryModel(input.Model); err != nil {
  85. return err
  86. }
  87. return nil
  88. }
  89. func ValidateAlertQueryModel(input monitor.MetricQuery) error {
  90. if len(input.Selects) == 0 {
  91. return merrors.NewArgIsEmptyErr("select")
  92. }
  93. if len(input.Database) == 0 {
  94. return merrors.NewArgIsEmptyErr("database")
  95. }
  96. if len(input.Measurement) == 0 {
  97. return merrors.NewArgIsEmptyErr("measurement")
  98. }
  99. return nil
  100. }
  101. func ValidateSelectOfMetricQuery(input monitor.AlertQuery) error {
  102. if err := ValidateFromAndToValue(input); err != nil {
  103. return errors.Wrap(err, "ValidateFromAndToValue")
  104. }
  105. if err := ValidateAlertQueryModel(input.Model); err != nil {
  106. return errors.Wrap(err, "ValidateAlertQueryModel")
  107. }
  108. for _, sel := range input.Model.Selects {
  109. if len(sel) == 0 {
  110. return httperrors.NewInputParameterError("select for nothing in query")
  111. }
  112. }
  113. if input.ResultReducer != nil {
  114. if !monitor.ValidateReducerTypes.Has(input.ResultReducer.Type) {
  115. return httperrors.NewInputParameterError("invalid result reducer type %s", input.ResultReducer.Type)
  116. }
  117. }
  118. return nil
  119. }
  120. func ValidateFromAndToValue(input monitor.AlertQuery) error {
  121. if err := ValidateFromValue(input.From); err != nil {
  122. return errors.Wrap(err, "ValidateFromValue")
  123. }
  124. if err := ValidateToValue(input.To); err != nil {
  125. return errors.Wrap(err, "ValidateToValue")
  126. }
  127. return nil
  128. }
  129. func ValidateAlertConditionReducer(input monitor.Condition) error {
  130. return nil
  131. }
  132. func ValidateAlertConditionEvaluator(input monitor.Condition) error {
  133. typ := input.Type
  134. if typ == "" {
  135. return ErrMissingParameterType
  136. }
  137. if utils.IsInStringArray(string(typ), EvaluatorDefaultTypes) {
  138. return ValidateAlertConditionThresholdEvaluator(input)
  139. }
  140. if utils.IsInStringArray(string(typ), EvaluatorRangedTypes) {
  141. return ValidateAlertConditionRangedEvaluator(input)
  142. }
  143. if typ != "no_value" {
  144. return errors.Wrapf(ErrInvalidEvaluatorType, "type: %s", typ)
  145. }
  146. return nil
  147. }
  148. func ValidateAlertConditionType(typ string) error {
  149. if typ == "" {
  150. return httperrors.NewInputParameterError("alert condition type is empty")
  151. }
  152. if !utils.IsInStringArray(typ, ConditionTypes) {
  153. return httperrors.NewInputParameterError("Unkown alert condition type: %s", typ)
  154. }
  155. return nil
  156. }
  157. func ValidateAlertConditionThresholdEvaluator(input monitor.Condition) error {
  158. if len(input.Params) == 0 {
  159. return errors.Wrapf(ErrMissingParameterThreshold, "Evaluator %s", HumanThresholdType(monitor.EvaluatorType(input.Type)))
  160. }
  161. return nil
  162. }
  163. func ValidateAlertConditionRangedEvaluator(input monitor.Condition) error {
  164. if len(input.Params) == 0 {
  165. return errors.Wrapf(ErrMissingParameterThreshold, "Evaluator %s", HumanThresholdType(monitor.EvaluatorType(input.Type)))
  166. }
  167. if len(input.Params) == 1 {
  168. return errors.Wrap(ErrMissingParameterThreshold, "RangedEvaluator parameter second parameter is missing")
  169. }
  170. return nil
  171. }
  172. // HumanThresholdType converts a threshold "type" string to a string that matches the UI
  173. // so errors are less confusing.
  174. func HumanThresholdType(typ monitor.EvaluatorType) string {
  175. switch typ {
  176. case monitor.EvaluatorTypeGT:
  177. return "IS ABOVE"
  178. case monitor.EvaluatorTypeLT:
  179. return "IS BELOW"
  180. case monitor.EvaluatorTypeWithinRange:
  181. return "IS WITHIN RANGE"
  182. case monitor.EvaluatorTypeOutsideRange:
  183. return "IS OUTSIDE RANGE"
  184. case monitor.EvaluatorTypeEQ:
  185. return "IS EQUAL TO"
  186. }
  187. return ""
  188. }
  189. func ValidateFromValue(from string) error {
  190. _, ok := tsdb.TryParseUnixMsEpoch(from)
  191. if ok {
  192. return nil
  193. }
  194. fromRaw := strings.Replace(from, "now-", "", 1)
  195. fromRawStr := "-" + fromRaw
  196. _, err := time.ParseDuration(fromRawStr)
  197. if err != nil {
  198. return errors.Wrapf(err, "parse duration: %s", fromRawStr)
  199. }
  200. return nil
  201. }
  202. func ValidateToValue(to string) error {
  203. if to == "now" {
  204. return nil
  205. } else if strings.HasPrefix(to, "now-") {
  206. withoutNow := strings.Replace(to, "now-", "", 1)
  207. _, err := time.ParseDuration("-" + withoutNow)
  208. if err == nil {
  209. return nil
  210. }
  211. }
  212. _, ok := tsdb.TryParseUnixMsEpoch(to)
  213. if ok {
  214. return nil
  215. }
  216. _, err := time.ParseDuration(to)
  217. return errors.Wrapf(err, "parse to: %s", to)
  218. }