mathreducer.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 conditions
  15. import (
  16. "math"
  17. "sort"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/onecloud/pkg/apis/monitor"
  20. )
  21. type mathReducer struct {
  22. // Type is how the timeseries should be reduced.
  23. // Ex: avg, sum, max, min, count
  24. Type string
  25. Opt string
  26. Params []float64
  27. }
  28. func (s *mathReducer) GetParams() []float64 {
  29. return s.Params
  30. }
  31. func (s *mathReducer) GetType() monitor.ReducerType {
  32. return monitor.ReducerType(s.Type)
  33. }
  34. func (s *mathReducer) Reduce(series *monitor.TimeSeries) (*float64, []string) {
  35. if len(series.Points) == 0 {
  36. return nil, nil
  37. }
  38. value := float64(0)
  39. allNull := true
  40. valArr := make([]string, 0)
  41. switch s.Type {
  42. case "avg":
  43. validPointsCount := 0
  44. for _, point := range series.Points {
  45. if point.IsValids() {
  46. values := point.Values()
  47. tem, err := s.mathValue(values)
  48. if err != nil {
  49. return nil, valArr
  50. }
  51. value += tem
  52. validPointsCount++
  53. allNull = false
  54. }
  55. }
  56. if validPointsCount > 0 {
  57. value = value / float64(validPointsCount)
  58. }
  59. case "sum":
  60. for _, point := range series.Points {
  61. if point.IsValids() {
  62. values := point.Values()
  63. tem, err := s.mathValue(values)
  64. if err != nil {
  65. return nil, valArr
  66. }
  67. value += tem
  68. allNull = false
  69. }
  70. }
  71. case "min":
  72. value = math.MaxFloat64
  73. for _, point := range series.Points {
  74. if point.IsValids() {
  75. allNull = false
  76. values := point.Values()
  77. tem, err := s.mathValue(values)
  78. if err != nil {
  79. return nil, valArr
  80. }
  81. if value > tem {
  82. value = tem
  83. }
  84. }
  85. }
  86. case "max":
  87. value = -math.MaxFloat64
  88. for _, point := range series.Points {
  89. if point.IsValids() {
  90. allNull = false
  91. values := point.Values()
  92. tem, err := s.mathValue(values)
  93. if err != nil {
  94. return nil, valArr
  95. }
  96. if value < tem {
  97. value = tem
  98. }
  99. }
  100. }
  101. case "count":
  102. value = float64(len(series.Points))
  103. allNull = false
  104. case "last":
  105. points := series.Points
  106. for i := len(points) - 1; i >= 0; i-- {
  107. if points[i].IsValids() {
  108. values := points[i].Values()
  109. tem, err := s.mathValue(values)
  110. if err != nil {
  111. return nil, valArr
  112. }
  113. value = tem
  114. allNull = false
  115. break
  116. }
  117. }
  118. case "median":
  119. var values []float64
  120. for _, point := range series.Points {
  121. if point.IsValids() {
  122. allNull = false
  123. values := point.Values()
  124. tem, err := s.mathValue(values)
  125. if err != nil {
  126. return nil, valArr
  127. }
  128. values = append(values, tem)
  129. }
  130. }
  131. if len(values) >= 1 {
  132. sort.Float64s(values)
  133. length := len(values)
  134. if length%2 == 1 {
  135. value = values[(length-1)/2]
  136. } else {
  137. value = (values[(length/2)-1] + values[length/2]) / 2
  138. }
  139. }
  140. case "diff":
  141. allNull, value = calculateDiff(series, allNull, value, diff)
  142. case "delta":
  143. allNull, value = calculateDelta(series, allNull, value)
  144. case "percent_diff":
  145. allNull, value = calculateDiff(series, allNull, value, percentDiff)
  146. case "count_non_null":
  147. for _, v := range series.Points {
  148. if v.IsValids() {
  149. value++
  150. }
  151. }
  152. if value > 0 {
  153. allNull = false
  154. }
  155. }
  156. if allNull {
  157. return nil, valArr
  158. }
  159. return &value, valArr
  160. }
  161. func (reducer *mathReducer) mathValue(values []float64) (float64, error) {
  162. value := float64(0)
  163. switch reducer.Opt {
  164. case "/":
  165. if len(values) < 2 {
  166. return value, errors.Errorf("point values length is too short")
  167. }
  168. if values[1] == 0 {
  169. return 1, nil
  170. }
  171. return values[0] / values[1], nil
  172. }
  173. return value, errors.Errorf("the reducer operator:%s is ilegal", reducer.Opt)
  174. }
  175. func newMathReducer(cond *monitor.Condition) (*mathReducer, error) {
  176. return &mathReducer{
  177. Type: cond.Type,
  178. Opt: cond.Operators[0],
  179. Params: cond.Params,
  180. }, nil
  181. }