migration_alert.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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 models
  15. import (
  16. "context"
  17. "database/sql"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/sqlchemy"
  22. "yunion.io/x/onecloud/pkg/apis/monitor"
  23. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  24. "yunion.io/x/onecloud/pkg/httperrors"
  25. "yunion.io/x/onecloud/pkg/mcclient"
  26. )
  27. var (
  28. migrationAlertMan *SMigrationAlertManager
  29. )
  30. func init() {
  31. migrationAlertMan = GetMigrationAlertManager()
  32. }
  33. func GetMigrationAlertManager() *SMigrationAlertManager {
  34. if migrationAlertMan != nil {
  35. return migrationAlertMan
  36. }
  37. migrationAlertMan = &SMigrationAlertManager{
  38. SAlertManager: *NewAlertManager(SMigrationAlert{}, "migrationalert", "migrationalerts"),
  39. }
  40. migrationAlertMan.SetVirtualObject(migrationAlertMan)
  41. return migrationAlertMan
  42. }
  43. // +onecloud:swagger-gen-model-singular=migrationalert
  44. // +onecloud:swagger-gen-model-plural=migrationalerts
  45. type SMigrationAlertManager struct {
  46. SAlertManager
  47. }
  48. type SMigrationAlert struct {
  49. SAlert
  50. MetricType string `create:"admin_required" list:"admin" get:"admin"`
  51. MigrateNotes jsonutils.JSONObject `nullable:"true" list:"admin" get:"admin" update:"admin" create:"admin_optional"`
  52. }
  53. type MigrateNoteGuest struct {
  54. Id string `json:"id"`
  55. Name string `json:"name"`
  56. HostId string `json:"host_id"`
  57. Host string `json:"host"`
  58. VCPUCount int `json:"vcpu_count"`
  59. VMemSize int `json:"vmem_size"`
  60. Score float64 `json:"score"`
  61. }
  62. type MigrateNoteTarget struct {
  63. Id string `json:"id"`
  64. Name string `json:"name"`
  65. Score float64 `json:"score"`
  66. }
  67. type MigrateNote struct {
  68. Guest *MigrateNoteGuest `json:"guest"`
  69. Target *MigrateNoteTarget `json:"target_host"`
  70. Error string `json:"error"`
  71. }
  72. func (m *SMigrationAlertManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query monitor.MigrationAlertListInput) (*sqlchemy.SQuery, error) {
  73. q, err := m.SAlertManager.ListItemFilter(ctx, q, userCred, query.AlertListInput)
  74. if err != nil {
  75. return nil, err
  76. }
  77. if len(query.MetricType) > 0 {
  78. q = q.Equals("metric_type", query.MetricType)
  79. }
  80. q = q.Equals("used_by", AlertNotificationUsedByMigrationAlert)
  81. return q, nil
  82. }
  83. func (m *SMigrationAlertManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *monitor.MigrationAlertCreateInput) (*monitor.MigrationAlertCreateInput, error) {
  84. if input.Period == "" {
  85. input.Period = "5m"
  86. }
  87. if _, err := time.ParseDuration(input.Period); err != nil {
  88. return nil, httperrors.NewInputParameterError("Invalid period format: %s", input.Period)
  89. }
  90. if err := monitor.IsValidMigrationAlertMetricType(input.MetricType); err != nil {
  91. return nil, httperrors.NewInputParameterError("Invalid metric_type %v", err)
  92. }
  93. if input.MigrationSettings == nil {
  94. input.MigrationSettings = &monitor.MigrationAlertSettings{
  95. Source: new(monitor.MigrationAlertSettingsSource),
  96. Target: new(monitor.MigrationAlertSettingsTarget),
  97. }
  98. }
  99. if err := m.ValidateMigrationSettings(input.MigrationSettings); err != nil {
  100. return nil, errors.Wrap(err, "validate migration settings")
  101. }
  102. aInput := *(input.ToAlertCreateInput())
  103. aInput, err := AlertManager.ValidateCreateData(ctx, userCred, nil, query, aInput)
  104. if err != nil {
  105. return input, errors.Wrap(err, "AlertManager.ValidateCreateData")
  106. }
  107. input.AlertCreateInput = aInput
  108. return input, nil
  109. }
  110. func (m *SMigrationAlertManager) ValidateMigrationSettings(s *monitor.MigrationAlertSettings) error {
  111. if s.Source != nil {
  112. if err := m.ValidateMigrationSettingsSource(s.Source); err != nil {
  113. return errors.Wrap(err, "validate source")
  114. }
  115. }
  116. if s.Target != nil {
  117. if err := m.ValidateMigrationSettingsTarget(s.Target); err != nil {
  118. return errors.Wrap(err, "validate target")
  119. }
  120. }
  121. return nil
  122. }
  123. func (m *SMigrationAlertManager) GetResourceByIdOrName(rType string, id string) (jsonutils.JSONObject, error) {
  124. ok, objs := MonitorResourceManager.GetResourceObjByResType(rType)
  125. if !ok {
  126. return nil, errors.Errorf("Get by %q", rType)
  127. }
  128. for _, obj := range objs {
  129. name, _ := obj.GetString("name")
  130. if name == id {
  131. return obj, nil
  132. }
  133. objId, _ := obj.GetString("id")
  134. if objId == id {
  135. return obj, nil
  136. }
  137. }
  138. return nil, errors.Errorf("Not found resource %q by %q", rType, id)
  139. }
  140. func (m *SMigrationAlertManager) GetHostByIdOrName(id string) (jsonutils.JSONObject, error) {
  141. return m.GetResourceByIdOrName(monitor.METRIC_RES_TYPE_HOST, id)
  142. }
  143. func (m *SMigrationAlertManager) GetGuestByIdOrName(id string) (jsonutils.JSONObject, error) {
  144. return m.GetResourceByIdOrName(monitor.METRIC_RES_TYPE_GUEST, id)
  145. }
  146. func (m *SMigrationAlertManager) validateResource(vf func(idOrName string) (jsonutils.JSONObject, error), ids []string) ([]string, error) {
  147. nIds := make([]string, len(ids))
  148. for idx, idName := range ids {
  149. obj, err := vf(idName)
  150. if err != nil {
  151. return nil, errors.Wrapf(err, "find by %s", idName)
  152. }
  153. id, err := obj.GetString("id")
  154. if err != nil {
  155. return nil, errors.Wrap(err, "get id")
  156. }
  157. nIds[idx] = id
  158. }
  159. return nIds, nil
  160. }
  161. func (m *SMigrationAlertManager) ValidateMigrationSettingsSource(input *monitor.MigrationAlertSettingsSource) error {
  162. if len(input.HostIds) != 0 {
  163. hIds, err := m.validateResource(m.GetHostByIdOrName, input.HostIds)
  164. if err != nil {
  165. return errors.Wrap(err, "validate host")
  166. }
  167. input.HostIds = hIds
  168. }
  169. if len(input.GuestIds) != 0 {
  170. gIds, err := m.validateResource(m.GetGuestByIdOrName, input.GuestIds)
  171. if err != nil {
  172. return errors.Wrap(err, "validate guest")
  173. }
  174. input.GuestIds = gIds
  175. }
  176. return nil
  177. }
  178. func (m *SMigrationAlertManager) ValidateMigrationSettingsTarget(input *monitor.MigrationAlertSettingsTarget) error {
  179. if len(input.HostIds) != 0 {
  180. hIds, err := m.validateResource(m.GetHostByIdOrName, input.HostIds)
  181. if err != nil {
  182. return errors.Wrap(err, "validate host")
  183. }
  184. input.HostIds = hIds
  185. }
  186. return nil
  187. }
  188. func (alert *SMigrationAlert) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  189. out := new(monitor.MigrationAlertCreateInput)
  190. if err := data.Unmarshal(out); err != nil {
  191. return errors.Wrap(err, "Unmarshal to MigrationAlertCreateInput")
  192. }
  193. fs := out.GetMetricDriver().GetQueryFields()
  194. alert.ResType = string(fs.ResourceType)
  195. alert.UsedBy = AlertNotificationUsedByMigrationAlert
  196. if err := alert.SAlert.CustomizeCreate(ctx, userCred, ownerId, query, data); err != nil {
  197. return errors.Wrap(err, "SAlert.CustomizeCreate")
  198. }
  199. return alert.CreateNotification(ctx, userCred)
  200. }
  201. func (m *SMigrationAlertManager) FetchAllMigrationAlerts() ([]SMigrationAlert, error) {
  202. objs := make([]SMigrationAlert, 0)
  203. q := m.Query()
  204. q = q.IsTrue("enabled")
  205. err := db.FetchModelObjects(m, q, &objs)
  206. if err != nil && err != sql.ErrNoRows {
  207. return nil, errors.Wrap(err, "db.FetchModelObjects")
  208. }
  209. return objs, nil
  210. }
  211. func (m *SMigrationAlertManager) GetInMigrationAlerts() ([]*SMigrationAlert, error) {
  212. alerts, err := m.FetchAllMigrationAlerts()
  213. if err != nil {
  214. return nil, errors.Wrap(err, "FetchAllMigrationAlerts")
  215. }
  216. objs := make([]*SMigrationAlert, 0)
  217. for _, a := range alerts {
  218. if ok, _, _ := a.IsInMigrationProcess(); ok {
  219. tmp := a
  220. objs = append(objs, &tmp)
  221. }
  222. }
  223. return objs, nil
  224. }
  225. func (alert *SMigrationAlert) GetMigrateNotes() (map[string]MigrateNote, error) {
  226. if alert.MigrateNotes == nil {
  227. return make(map[string]MigrateNote, 0), nil
  228. }
  229. objs := make(map[string]MigrateNote, 0)
  230. if err := alert.MigrateNotes.Unmarshal(&objs); err != nil {
  231. return nil, errors.Wrap(err, "Unmarshal")
  232. }
  233. return objs, nil
  234. }
  235. func (alert *SMigrationAlert) SetMigrateNote(ctx context.Context, ns *MigrateNote, isDelete bool) error {
  236. _, err := db.UpdateWithLock(ctx, alert, func() error {
  237. curNotes, err := alert.GetMigrateNotes()
  238. if err != nil {
  239. return errors.Wrap(err, "GetMigrateNotes")
  240. }
  241. if isDelete {
  242. delete(curNotes, ns.Guest.Id)
  243. } else {
  244. curNotes[ns.Guest.Id] = *ns
  245. }
  246. alert.MigrateNotes = jsonutils.Marshal(curNotes)
  247. return nil
  248. })
  249. return err
  250. }
  251. func (alert *SMigrationAlert) IsInMigrationProcess() (bool, map[string]MigrateNote, error) {
  252. notes, err := alert.GetMigrateNotes()
  253. if err != nil {
  254. return false, nil, errors.Wrap(err, "GetMigrateNotes")
  255. }
  256. if len(notes) == 0 {
  257. return false, nil, nil
  258. }
  259. return true, nil, nil
  260. }
  261. func (alert *SMigrationAlert) GetMigrationSettings() (*monitor.MigrationAlertSettings, error) {
  262. if alert.CustomizeConfig == nil {
  263. return nil, errors.Errorf("CustomizeConfig is nil")
  264. }
  265. out := new(monitor.MigrationAlertSettings)
  266. if err := alert.CustomizeConfig.Unmarshal(out); err != nil {
  267. return nil, err
  268. }
  269. return out, nil
  270. }
  271. func (alert *SMigrationAlert) CreateNotification(ctx context.Context, userCred mcclient.TokenCredential) error {
  272. if alert.Id == "" {
  273. alert.Id = db.DefaultUUIDGenerator()
  274. }
  275. noti, err := NotificationManager.CreateAutoMigrationNotification(ctx, userCred, alert)
  276. if err != nil {
  277. return errors.Wrap(err, "CreateAutoMigrationNotification")
  278. }
  279. if _, err := alert.AttachNotification(ctx, userCred, noti, monitor.AlertNotificationStateUnknown, ""); err != nil {
  280. return errors.Wrap(err, "alert.AttachNotification")
  281. }
  282. return nil
  283. }
  284. func (alert *SMigrationAlert) GetMetricType() monitor.MigrationAlertMetricType {
  285. return monitor.MigrationAlertMetricType(alert.MetricType)
  286. }
  287. func (alert *SMigrationAlert) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  288. if err := alert.deleteNotifications(ctx, userCred, query, data); err != nil {
  289. return errors.Wrap(err, "delete related notification")
  290. }
  291. return alert.SAlert.CustomizeDelete(ctx, userCred, query, data)
  292. }