cpu.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 balancer
  15. import (
  16. "yunion.io/x/jsonutils"
  17. "yunion.io/x/log"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/pkg/util/sets"
  20. "yunion.io/x/onecloud/pkg/apis/monitor"
  21. "yunion.io/x/onecloud/pkg/monitor/tsdb"
  22. )
  23. func setHostCurrent(host IHost, vals map[string]float64, key string) error {
  24. val, ok := vals[key]
  25. if !ok {
  26. return errors.Errorf("not found %q in vals %#v", key, vals)
  27. }
  28. host.SetCurrent(val)
  29. return nil
  30. }
  31. type cpuUsageActive struct{}
  32. func newCPUUsageActive() IMetricDriver {
  33. return &cpuUsageActive{}
  34. }
  35. func (c *cpuUsageActive) GetType() monitor.MigrationAlertMetricType {
  36. return monitor.MigrationAlertMetricTypeCPUUsageActive
  37. }
  38. func (c *cpuUsageActive) GetTsdbQuery() *TsdbQuery {
  39. return &TsdbQuery{
  40. Database: monitor.METRIC_DATABASE_TELE,
  41. Measurement: "cpu",
  42. Fields: []string{"usage_active"},
  43. }
  44. }
  45. func (c *cpuUsageActive) GetCandidate(obj jsonutils.JSONObject, host IHost, ds *tsdb.DataSource) (ICandidate, error) {
  46. return newCPUCandidate(obj, host.GetHostResource(), ds)
  47. }
  48. func (c *cpuUsageActive) SetHostCurrent(host IHost, vals map[string]float64) error {
  49. return setHostCurrent(host, vals, "usage_active")
  50. }
  51. func (c *cpuUsageActive) GetTarget(host jsonutils.JSONObject) (ITarget, error) {
  52. return newTargetCPUHost(host)
  53. }
  54. func (ma *cpuUsageActive) GetCondition(s *monitor.AlertSetting) (ICondition, error) {
  55. t, err := GetAlertSettingThreshold(s)
  56. if err != nil {
  57. return nil, errors.Wrap(err, "GetAlertSettingThreshold")
  58. }
  59. return newCPUCond(t), nil
  60. }
  61. // cpuCondition implements ICondition
  62. type cpuCondition struct {
  63. value float64
  64. }
  65. func newCPUCond(val float64) ICondition {
  66. return &cpuCondition{
  67. value: val,
  68. }
  69. }
  70. func (c *cpuCondition) GetThreshold() float64 {
  71. return c.value
  72. }
  73. func (c *cpuCondition) GetSourceThresholdDelta(threshold float64, host IHost) float64 {
  74. // cpu.usage_active
  75. return host.GetCurrent() - threshold
  76. }
  77. func (m *cpuCondition) IsFitTarget(settings *monitor.MigrationAlertSettings, t ITarget, c ICandidate) error {
  78. src := settings.Source
  79. srcHostIds := []string{}
  80. if src != nil {
  81. srcHostIds = src.HostIds
  82. }
  83. tCPUCnt := t.(*targetCPUHost).GetCPUCount()
  84. tScore := t.GetCurrent() + c.(*cpuCandidate).getTargetScore(tCPUCnt)
  85. ltThreshold := tScore < m.GetThreshold()
  86. if ltThreshold {
  87. return nil
  88. }
  89. MAX_THRESHOLD := 95.0
  90. // only when srcHostIds isn't empty
  91. if len(srcHostIds) != 0 {
  92. if !ltThreshold && !sets.NewString(srcHostIds...).Has(t.GetId()) && tScore < MAX_THRESHOLD {
  93. // if target host is not in source specified hosts and calculated score is less than MAX_THRESHOLD
  94. log.Infof("let host:%s:current(%f) + guest:%s:score(%f) < MAX_THRESHOLD(%f) to fit target, because it's not in source specified hosts", t.GetName(), t.GetCurrent(), c.GetName(), c.GetScore(), MAX_THRESHOLD)
  95. return nil
  96. }
  97. }
  98. return errors.Errorf("host:%s:current(%f) + guest:%s:score(%f) >= threshold(%f)", t.GetName(), t.GetCurrent(), c.GetName(), c.GetScore(), m.GetThreshold())
  99. }
  100. // cpuCandidate implements ICandidate
  101. type cpuCandidate struct {
  102. *guestResource
  103. usageActive float64
  104. guestCPUCount int
  105. hostCPUCount int
  106. }
  107. func newCPUCandidate(gst jsonutils.JSONObject, host *HostResource, ds *tsdb.DataSource) (ICandidate, error) {
  108. res, err := newGuestResource(gst, host.GetName())
  109. if err != nil {
  110. return nil, errors.Wrap(err, "newGuestResource")
  111. }
  112. gstCPUCount, err := res.guest.Int("vcpu_count")
  113. if err != nil {
  114. return nil, errors.Wrap(err, "get vcpu_count")
  115. }
  116. // fetch metric from influxdb
  117. metrics, err := InfluxdbQuery(ds, "vm_id", []IResource{res}, &TsdbQuery{
  118. Database: monitor.METRIC_DATABASE_TELE,
  119. Measurement: "vm_cpu",
  120. Fields: []string{"usage_active"},
  121. })
  122. if err != nil {
  123. return nil, errors.Wrapf(err, "InfluxdbQuery guest %q(%q)", res.GetName(), res.GetId())
  124. }
  125. metric := metrics.Get(res.GetId())
  126. if metric == nil {
  127. return nil, errors.Errorf("not found resource %q metric from %#v", res.GetId(), metrics.indexes)
  128. }
  129. usage := metric.Values["usage_active"]
  130. return &cpuCandidate{
  131. guestResource: res,
  132. usageActive: usage,
  133. guestCPUCount: int(gstCPUCount),
  134. hostCPUCount: int(host.cpuCount),
  135. }, nil
  136. }
  137. func (c cpuCandidate) GetScore() float64 {
  138. return c.getTargetScore(c.hostCPUCount)
  139. }
  140. func (c cpuCandidate) getTargetScore(tCPUCnt int) float64 {
  141. score := c.usageActive * (float64(c.guestCPUCount) / float64(tCPUCnt))
  142. return score
  143. }
  144. type cpuHost struct {
  145. *HostResource
  146. usageActive float64
  147. }
  148. func newCPUHost(obj jsonutils.JSONObject) (IHost, error) {
  149. host, err := newHostResource(obj)
  150. if err != nil {
  151. return nil, errors.Wrap(err, "newHostResource")
  152. }
  153. return &cpuHost{
  154. HostResource: host,
  155. }, nil
  156. }
  157. func (h *cpuHost) GetCurrent() float64 {
  158. return h.usageActive
  159. }
  160. func (h *cpuHost) SetCurrent(val float64) IHost {
  161. h.usageActive = val
  162. return h
  163. }
  164. func (h *cpuHost) Compare(oh IHost) bool {
  165. return h.GetCurrent() < oh.GetCurrent()
  166. }
  167. type targetCPUHost struct {
  168. IHost
  169. }
  170. func newTargetCPUHost(obj jsonutils.JSONObject) (ITarget, error) {
  171. host, err := newCPUHost(obj)
  172. if err != nil {
  173. return nil, errors.Wrap(err, "newCPUHost")
  174. }
  175. ts := &targetCPUHost{
  176. IHost: host,
  177. }
  178. return ts, nil
  179. }
  180. func (ts *targetCPUHost) Selected(c ICandidate) ITarget {
  181. ts.SetCurrent(ts.GetCurrent() + c.GetScore())
  182. return ts
  183. }
  184. func (ts *targetCPUHost) GetCPUCount() int {
  185. return int(ts.IHost.(*cpuHost).cpuCount)
  186. }