alert.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/rbacscope"
  24. "yunion.io/x/sqlchemy"
  25. "yunion.io/x/onecloud/pkg/apis"
  26. "yunion.io/x/onecloud/pkg/apis/monitor"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  28. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  29. "yunion.io/x/onecloud/pkg/httperrors"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/monitor/validators"
  32. "yunion.io/x/onecloud/pkg/util/stringutils2"
  33. )
  34. const (
  35. AlertMetadataTitle = "alert_title"
  36. AlertMetadataChannel = "alert_channel"
  37. )
  38. var (
  39. AlertManager *SAlertManager
  40. )
  41. func init() {
  42. AlertManager = NewAlertManager(SAlert{}, "alert", "alerts")
  43. }
  44. type AlertTestRunner interface {
  45. DoTest(ruleDef *SAlert, userCred mcclient.TokenCredential, input monitor.AlertTestRunInput) (*monitor.AlertTestRunOutput, error)
  46. }
  47. type SAlertManager struct {
  48. //db.SVirtualResourceBaseManager
  49. db.SEnabledResourceBaseManager
  50. db.SStatusStandaloneResourceBaseManager
  51. SMonitorScopedResourceManager
  52. //db.SStatusResourceBaseManager
  53. tester AlertTestRunner
  54. }
  55. func NewAlertManager(dt interface{}, keyword, keywordPlural string) *SAlertManager {
  56. man := &SAlertManager{
  57. SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
  58. dt,
  59. "alerts_tbl",
  60. keyword,
  61. keywordPlural),
  62. }
  63. man.SetVirtualObject(man)
  64. return man
  65. }
  66. func (man *SAlertManager) SetTester(tester AlertTestRunner) {
  67. man.tester = tester
  68. }
  69. func (man *SAlertManager) GetTester() AlertTestRunner {
  70. return man.tester
  71. }
  72. func (manager *SAlertManager) NamespaceScope() rbacscope.TRbacScope {
  73. return rbacscope.ScopeSystem
  74. }
  75. func (manager *SAlertManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  76. q, err := manager.SStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  77. if err != nil {
  78. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemExportKeys")
  79. }
  80. q, err = manager.SScopedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  81. if err != nil {
  82. return nil, errors.Wrap(err, "SScopedResourceBaseManager.ListItemExportKeys")
  83. }
  84. return q, nil
  85. }
  86. func (man *SAlertManager) FetchAllAlerts() ([]SAlert, error) {
  87. objs := make([]SAlert, 0)
  88. q := man.Query()
  89. q = q.IsTrue("enabled")
  90. err := db.FetchModelObjects(man, q, &objs)
  91. if err != nil && err != sql.ErrNoRows {
  92. return nil, errors.Wrap(err, "db.FetchModelObjects")
  93. }
  94. return objs, nil
  95. }
  96. type SAlert struct {
  97. //db.SVirtualResourceBase
  98. db.SEnabledResourceBase
  99. db.SStatusStandaloneResourceBase
  100. SMonitorScopedResource
  101. //db.SStatusResourceBase
  102. // Frequency is evaluate period
  103. Frequency int64 `nullable:"false" list:"user" create:"required" update:"user"`
  104. Settings *monitor.AlertSetting `nullable:"false" list:"user" create:"required" update:"user" length:"medium"`
  105. Level string `charset:"ascii" width:"36" nullable:"false" default:"normal" list:"user" update:"user"`
  106. Message string `charset:"utf8" list:"user" create:"optional" update:"user"`
  107. UsedBy string `charset:"ascii" create:"optional" list:"user"`
  108. DisableNotifyRecovery bool `nullable:"false" default:"false" list:"user" create:"optional" update:"user"`
  109. // Silenced bool
  110. ExecutionError string `charset:"utf8" list:"user"`
  111. // If an alert rule has a configured `For` and the query violates the configured threshold
  112. // it will first go from `OK` to `Pending`. Going from `OK` to `Pending` will not send any
  113. // notifications. Once the alert rule has been firing for more than `For` duration, it will
  114. // change to `Alerting` and send alert notifications.
  115. For int64 `nullable:"false" list:"user" update:"user"`
  116. EvalData jsonutils.JSONObject `list:"user" length:"medium"`
  117. State string `width:"36" charset:"ascii" nullable:"false" default:"unknown" list:"user" update:"user"`
  118. NoDataState string `width:"36" charset:"ascii" nullable:"false" default:"no_data" create:"optional" list:"user" update:"user"`
  119. ExecutionErrorState string `width:"36" charset:"ascii" nullable:"false" default:"alerting" create:"optional" list:"user" update:"user"`
  120. LastStateChange time.Time `list:"user"`
  121. StateChanges int `default:"0" nullable:"false" list:"user"`
  122. CustomizeConfig jsonutils.JSONObject `list:"user" create:"optional" update:"user" length:"medium"`
  123. ResType string `width:"32" list:"user" update:"user"`
  124. // 报警原因
  125. Reason string `length:"0" charset:"utf8" get:"user" list:"user" update:"user" create:"optional" json:"reason"`
  126. }
  127. func (alert *SAlert) IsEnable() bool {
  128. return alert.Enabled.Bool()
  129. }
  130. func (alert *SAlert) SetEnable() error {
  131. _, err := db.Update(alert, func() error {
  132. alert.SetEnabled(true)
  133. alert.State = string(monitor.AlertStatePending)
  134. return nil
  135. })
  136. return err
  137. }
  138. func (alert *SAlert) SetDisable() error {
  139. _, err := db.Update(alert, func() error {
  140. alert.SEnabledResourceBase.SetEnabled(false)
  141. alert.State = string(monitor.AlertStatePaused)
  142. return nil
  143. })
  144. return err
  145. }
  146. func (alert *SAlert) SetUsedBy(usedBy string) error {
  147. _, err := db.Update(alert, func() error {
  148. alert.UsedBy = usedBy
  149. return nil
  150. })
  151. return err
  152. }
  153. func (alert *SAlert) SetTitle(ctx context.Context, t string) error {
  154. return alert.SetMetadata(ctx, AlertMetadataTitle, t, nil)
  155. }
  156. func (alert *SAlert) GetTitle() string {
  157. return alert.GetMetadata(context.Background(), AlertMetadataTitle, nil)
  158. }
  159. func (alert *SAlert) SetChannel(ctx context.Context, channel []string) error {
  160. return alert.SetMetadata(ctx, AlertMetadataChannel, jsonutils.Marshal(channel), nil)
  161. }
  162. func (alert *SAlert) GetChannel() []string {
  163. channelJson := alert.GetMetadataJson(context.Background(), AlertMetadataChannel, nil)
  164. if channelJson == nil {
  165. return []string{}
  166. }
  167. channel := []string{}
  168. if err := channelJson.Unmarshal(&channel); err != nil {
  169. log.Warningf("get channel failed when unmarshal: %v", err)
  170. }
  171. return channel
  172. }
  173. func (alert *SAlert) ShouldUpdateState(newState monitor.AlertStateType) bool {
  174. return monitor.AlertStateType(alert.State) != newState
  175. }
  176. func (alert *SAlert) GetSettings() (*monitor.AlertSetting, error) {
  177. return alert.Settings, nil
  178. }
  179. type AlertRuleTags map[string]AlertRuleTag
  180. type AlertRuleTag struct {
  181. Key string
  182. Value string
  183. }
  184. func setAlertDefaultSetting(setting *monitor.AlertSetting) *monitor.AlertSetting {
  185. for idx, cond := range setting.Conditions {
  186. cond = setAlertDefaultCondition(cond)
  187. setting.Conditions[idx] = cond
  188. }
  189. return setting
  190. }
  191. func setAlertDefaultCreateData(data monitor.AlertCreateInput) monitor.AlertCreateInput {
  192. setting := setAlertDefaultSetting(&data.Settings)
  193. data.Settings = *setting
  194. enable := true
  195. if data.Enabled == nil {
  196. data.Enabled = &enable
  197. }
  198. return data
  199. }
  200. func setAlertDefaultCondition(cond monitor.AlertCondition) monitor.AlertCondition {
  201. if cond.Type == "" {
  202. cond.Type = "query"
  203. }
  204. if cond.Query.To == "" {
  205. cond.Query.To = "now"
  206. }
  207. if cond.Operator == "" {
  208. cond.Operator = "and"
  209. }
  210. return cond
  211. }
  212. func (man *SAlertManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, _ jsonutils.JSONObject, data monitor.AlertCreateInput) (monitor.AlertCreateInput, error) {
  213. data = setAlertDefaultCreateData(data)
  214. if err := validators.ValidateAlertCreateInput(data); err != nil {
  215. return data, err
  216. }
  217. if err := man.validateStates(data.NoDataState, data.ExecutionErrorState); err != nil {
  218. return data, err
  219. }
  220. return data, nil
  221. }
  222. func (man *SAlertManager) validateStates(noData string, execErr string) error {
  223. if noData != "" {
  224. if err := man.validateNoDataState(monitor.NoDataOption(noData)); err != nil {
  225. return err
  226. }
  227. }
  228. if execErr != "" {
  229. if err := man.validateExecutionErrorState(monitor.ExecutionErrorOption(execErr)); err != nil {
  230. return err
  231. }
  232. }
  233. return nil
  234. }
  235. func (man *SAlertManager) validateNoDataState(state monitor.NoDataOption) error {
  236. if !state.IsValid() {
  237. return httperrors.NewInputParameterError("unsupported no_data_state %s", state)
  238. }
  239. return nil
  240. }
  241. func (man *SAlertManager) validateExecutionErrorState(state monitor.ExecutionErrorOption) error {
  242. if !state.IsValid() {
  243. return httperrors.NewInputParameterError("unsupported execution_error_state %s", state)
  244. }
  245. return nil
  246. }
  247. func (man *SAlertManager) ListItemFilter(
  248. ctx context.Context,
  249. q *sqlchemy.SQuery,
  250. userCred mcclient.TokenCredential,
  251. input monitor.AlertListInput,
  252. ) (*sqlchemy.SQuery, error) {
  253. var err error
  254. q, err = man.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StatusStandaloneResourceListInput)
  255. if err != nil {
  256. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
  257. }
  258. q, err = man.SScopedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.ScopedResourceBaseListInput)
  259. if err != nil {
  260. return nil, errors.Wrap(err, "SScopedResourceBaseManager.ListItemFilter")
  261. }
  262. q, err = man.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
  263. if err != nil {
  264. return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter")
  265. }
  266. if len(input.MonitorResourceId) != 0 {
  267. sq := MonitorResourceAlertManager.Query("alert_id").In("monitor_resource_id", input.MonitorResourceId).SubQuery()
  268. q = q.In("id", sq)
  269. }
  270. return q, nil
  271. }
  272. func (man *SAlertManager) OrderByExtraFields(
  273. ctx context.Context,
  274. q *sqlchemy.SQuery,
  275. userCred mcclient.TokenCredential,
  276. input monitor.AlertListInput,
  277. ) (*sqlchemy.SQuery, error) {
  278. var err error
  279. q, err = man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.StatusStandaloneResourceListInput)
  280. if err != nil {
  281. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
  282. }
  283. q, err = man.SScopedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.ScopedResourceBaseListInput)
  284. if err != nil {
  285. return nil, errors.Wrap(err, "SScopedResourceBaseManager.OrderByExtraFields")
  286. }
  287. return q, nil
  288. }
  289. func (man *SAlertManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  290. var err error
  291. q, err = man.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  292. if err == nil {
  293. return q, nil
  294. }
  295. q, err = man.SScopedResourceBaseManager.QueryDistinctExtraField(q, field)
  296. if err == nil {
  297. return q, nil
  298. }
  299. return q, httperrors.ErrNotFound
  300. }
  301. func (man *SAlertManager) FetchCustomizeColumns(
  302. ctx context.Context,
  303. userCred mcclient.TokenCredential,
  304. query jsonutils.JSONObject,
  305. objs []interface{},
  306. fields stringutils2.SSortedStrings,
  307. isList bool,
  308. ) []monitor.AlertDetails {
  309. rows := make([]monitor.AlertDetails, len(objs))
  310. stdRows := man.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  311. scopedRows := man.SScopedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  312. for i := range rows {
  313. rows[i] = monitor.AlertDetails{
  314. StatusStandaloneResourceDetails: stdRows[i],
  315. ScopedResourceBaseInfo: scopedRows[i],
  316. }
  317. }
  318. return rows
  319. }
  320. func (man *SAlertManager) GetAlert(id string) (*SAlert, error) {
  321. obj, err := man.FetchById(id)
  322. if err != nil {
  323. if errors.Cause(err) == sql.ErrNoRows {
  324. return nil, nil
  325. }
  326. return nil, err
  327. }
  328. return obj.(*SAlert), nil
  329. }
  330. func GetMeasurementField(metric string) (string, string, error) {
  331. parts := strings.Split(metric, ".")
  332. if len(parts) != 2 {
  333. return "", "", httperrors.NewInputParameterError("metric %s is invalid format, usage <measurement>.<field>", metric)
  334. }
  335. measurement, field := parts[0], parts[1]
  336. return measurement, field, nil
  337. }
  338. func IsQuerySelectHasField(selects monitor.MetricQuerySelect, field string) bool {
  339. for _, s := range selects {
  340. if s.Type == "field" && len(s.Params) == 1 {
  341. if s.Params[0] == field {
  342. return true
  343. }
  344. }
  345. }
  346. return false
  347. }
  348. func (man *SAlertManager) CustomizeFilterList(
  349. ctx context.Context, q *sqlchemy.SQuery,
  350. userCred mcclient.TokenCredential, query jsonutils.JSONObject) (
  351. *db.CustomizeListFilters, error) {
  352. filters := db.NewCustomizeListFilters()
  353. return filters, nil
  354. }
  355. func (alert *SAlert) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  356. alert.LastStateChange = time.Now()
  357. alert.State = string(monitor.AlertStateUnknown)
  358. return alert.SScopedResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  359. }
  360. func (alert *SAlert) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  361. alert.SetStatus(ctx, userCred, monitor.ALERT_STATUS_READY, "")
  362. }
  363. func (alert *SAlert) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
  364. err := db.EnabledPerformEnable(alert, ctx, userCred, true)
  365. if err != nil {
  366. return nil, errors.Wrap(err, "EnabledPerformEnable")
  367. }
  368. return nil, nil
  369. }
  370. func (alert *SAlert) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
  371. err := db.EnabledPerformEnable(alert, ctx, userCred, false)
  372. if err != nil {
  373. return nil, errors.Wrap(err, "EnabledPerformEnable")
  374. }
  375. return nil, nil
  376. }
  377. const (
  378. ErrAlertChannotChangeStateOnPaused = errors.Error("Cannot change state on pause alert")
  379. )
  380. func (alert *SAlert) GetExecutionErrorState() monitor.ExecutionErrorOption {
  381. return monitor.ExecutionErrorOption(alert.ExecutionErrorState)
  382. }
  383. func (alert *SAlert) GetNoDataState() monitor.NoDataOption {
  384. return monitor.NoDataOption(alert.NoDataState)
  385. }
  386. func (alert *SAlert) GetState() monitor.AlertStateType {
  387. return monitor.AlertStateType(alert.State)
  388. }
  389. type AlertSetStateInput struct {
  390. State monitor.AlertStateType
  391. EvalData jsonutils.JSONObject
  392. UpdateStateTime time.Time
  393. ExecutionError string
  394. }
  395. func (alert *SAlert) SetState(input AlertSetStateInput) error {
  396. if alert.State == string(monitor.AlertStatePaused) {
  397. return ErrAlertChannotChangeStateOnPaused
  398. }
  399. _, err := db.Update(alert, func() error {
  400. alert.State = string(input.State)
  401. if input.State != monitor.AlertStatePending {
  402. alert.LastStateChange = input.UpdateStateTime
  403. }
  404. alert.EvalData = input.EvalData
  405. alert.ExecutionError = input.ExecutionError
  406. alert.StateChanges = alert.StateChanges + 1
  407. return nil
  408. })
  409. return err
  410. }
  411. func (alert *SAlert) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input monitor.AlertUpdateInput) (monitor.AlertUpdateInput, error) {
  412. var err error
  413. input.StandaloneResourceBaseUpdateInput, err = alert.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
  414. if err != nil {
  415. return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
  416. }
  417. if err := AlertManager.validateStates(input.NoDataState, input.ExecutionErrorState); err != nil {
  418. return input, err
  419. }
  420. return input, nil
  421. }
  422. func (alert *SAlert) IsAttachNotification(noti *SNotification) (bool, error) {
  423. q := AlertNotificationManager.Query().Equals("notification_id", noti.GetId()).Equals("alert_id", alert.GetId())
  424. cnt, err := q.CountWithError()
  425. if err != nil {
  426. return false, err
  427. }
  428. return cnt > 0, nil
  429. }
  430. func (alert *SAlert) GetNotificationsQuery() *sqlchemy.SQuery {
  431. return AlertNotificationManager.Query().Equals("alert_id", alert.GetId())
  432. }
  433. func (alert *SAlert) GetNotifications() ([]SAlertnotification, error) {
  434. notis := make([]SAlertnotification, 0)
  435. q := alert.GetNotificationsQuery().Asc("index")
  436. if err := db.FetchModelObjects(AlertNotificationManager, q, &notis); err != nil {
  437. return nil, err
  438. }
  439. return notis, nil
  440. }
  441. func (alert *SAlert) deleteNotifications(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  442. notis, err := alert.GetNotifications()
  443. if err != nil {
  444. return err
  445. }
  446. for _, noti := range notis {
  447. conf, err := noti.GetNotification()
  448. if err != nil {
  449. return errors.Wrap(err, "GetNotification")
  450. }
  451. if err := conf.CustomizeDelete(ctx, userCred, query, data); err != nil {
  452. return errors.Wrapf(err, "notification %s(%s) CustomizeDelete", conf.GetName(), conf.GetId())
  453. }
  454. if err := noti.Detach(ctx, userCred); err != nil {
  455. return errors.Wrapf(err, "notification %s(%s) Detach ", conf.GetName(), conf.GetId())
  456. }
  457. if err := conf.Delete(ctx, userCred); err != nil {
  458. return errors.Wrapf(err, "notification %s(%s) Delete", conf.GetName(), conf.GetId())
  459. }
  460. }
  461. return nil
  462. }
  463. func (alert *SAlert) GetAlertResources() ([]*SAlertResource, error) {
  464. jRess := make([]SAlertResourceAlert, 0)
  465. jm := GetAlertResourceAlertManager()
  466. q := jm.Query().Equals("alert_id", alert.GetId())
  467. if err := db.FetchModelObjects(jm, q, &jRess); err != nil {
  468. return nil, err
  469. }
  470. ress := make([]*SAlertResource, len(jRess))
  471. for idx := range jRess {
  472. res, err := jRess[idx].GetAlertResource()
  473. if err != nil {
  474. return nil, errors.Wrapf(err, "get alert %s related alret resource", alert.GetName())
  475. }
  476. ress[idx] = res
  477. }
  478. return ress, nil
  479. }
  480. func (alert *SAlert) getNotificationIndex() (int8, error) {
  481. notis, err := alert.GetNotifications()
  482. if err != nil {
  483. return -1, err
  484. }
  485. var max uint
  486. for i := 0; i < len(notis); i++ {
  487. if uint(notis[i].Index) > max {
  488. max = uint(notis[i].Index)
  489. }
  490. }
  491. idxs := make([]int, max+1)
  492. for i := 0; i < len(notis); i++ {
  493. idxs[notis[i].Index] = 1
  494. }
  495. // find first idx not set
  496. for i := 0; i < len(idxs); i++ {
  497. if idxs[i] != 1 {
  498. return int8(i), nil
  499. }
  500. }
  501. return int8(max + 1), nil
  502. }
  503. func (alert *SAlert) AttachNotification(
  504. ctx context.Context,
  505. userCred mcclient.TokenCredential,
  506. noti *SNotification,
  507. state monitor.AlertNotificationStateType,
  508. usedBy string) (*SAlertnotification, error) {
  509. attached, err := alert.IsAttachNotification(noti)
  510. if err != nil {
  511. return nil, err
  512. }
  513. if attached {
  514. return nil, httperrors.NewNotAcceptableError("alert already attached to notification")
  515. }
  516. lockman.LockObject(ctx, alert)
  517. defer lockman.ReleaseObject(ctx, alert)
  518. alertNoti := new(SAlertnotification)
  519. alertNoti.AlertId = alert.GetId()
  520. alertNoti.Index, err = alert.getNotificationIndex()
  521. alertNoti.NotificationId = noti.GetId()
  522. if err != nil {
  523. return nil, err
  524. }
  525. alertNoti.State = string(state)
  526. alertNoti.UsedBy = usedBy
  527. if err := alertNoti.DoSave(ctx, userCred); err != nil {
  528. return nil, err
  529. }
  530. return alertNoti, nil
  531. }
  532. func (alert *SAlert) SetFor(forTime time.Duration) error {
  533. _, err := db.Update(alert, func() error {
  534. alert.For = int64(forTime)
  535. return nil
  536. })
  537. return err
  538. }
  539. func (alert *SAlert) PerformTestRun(
  540. ctx context.Context,
  541. userCred mcclient.TokenCredential,
  542. query jsonutils.JSONObject,
  543. input monitor.AlertTestRunInput,
  544. ) (*monitor.AlertTestRunOutput, error) {
  545. return alert.TestRunAlert(userCred, input)
  546. }
  547. func (alert *SAlert) TestRunAlert(userCred mcclient.TokenCredential, input monitor.AlertTestRunInput) (*monitor.AlertTestRunOutput, error) {
  548. return AlertManager.GetTester().DoTest(alert, userCred, input)
  549. }
  550. func (alert *SAlert) CustomizeDelete(
  551. ctx context.Context, userCred mcclient.TokenCredential,
  552. query jsonutils.JSONObject, data jsonutils.JSONObject,
  553. ) error {
  554. notis, err := alert.GetNotifications()
  555. if err != nil {
  556. return err
  557. }
  558. for _, noti := range notis {
  559. if err := noti.Detach(ctx, userCred); err != nil {
  560. return err
  561. }
  562. }
  563. alertRess, err := alert.GetAlertResources()
  564. if err != nil {
  565. return errors.Wrap(err, "get alert resources")
  566. }
  567. for _, res := range alertRess {
  568. if err := res.DetachAlert(ctx, userCred, alert.GetId()); err != nil {
  569. return errors.Wrapf(err, "detach alert resource %s", res.LogPrefix())
  570. }
  571. }
  572. return nil
  573. }
  574. func (alert *SAlert) PerformPause(
  575. ctx context.Context,
  576. userCred mcclient.TokenCredential,
  577. query jsonutils.JSONObject,
  578. input monitor.AlertPauseInput,
  579. ) (jsonutils.JSONObject, error) {
  580. curState := alert.GetState()
  581. if curState != monitor.AlertStatePaused && !input.Paused {
  582. return nil, httperrors.NewNotAcceptableError("Alert is already un-paused")
  583. }
  584. if curState == monitor.AlertStatePaused && input.Paused {
  585. return nil, httperrors.NewNotAcceptableError("Alert is already paused")
  586. }
  587. var newState monitor.AlertStateType
  588. if input.Paused {
  589. newState = monitor.AlertStatePaused
  590. } else {
  591. newState = monitor.AlertStateUnknown
  592. }
  593. err := alert.SetState(AlertSetStateInput{
  594. State: newState,
  595. })
  596. return nil, err
  597. }