commonalert.go 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902
  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. "fmt"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "golang.org/x/text/language"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/appctx"
  26. "yunion.io/x/pkg/errors"
  27. "yunion.io/x/pkg/gotypes"
  28. "yunion.io/x/pkg/util/rbacscope"
  29. "yunion.io/x/pkg/util/sets"
  30. "yunion.io/x/pkg/utils"
  31. "yunion.io/x/sqlchemy"
  32. "yunion.io/x/onecloud/pkg/apis"
  33. "yunion.io/x/onecloud/pkg/apis/monitor"
  34. notiapi "yunion.io/x/onecloud/pkg/apis/notify"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  36. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  37. "yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts"
  38. "yunion.io/x/onecloud/pkg/httperrors"
  39. "yunion.io/x/onecloud/pkg/mcclient"
  40. "yunion.io/x/onecloud/pkg/mcclient/auth"
  41. "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
  42. "yunion.io/x/onecloud/pkg/mcclient/modules/yunionconf"
  43. merrors "yunion.io/x/onecloud/pkg/monitor/errors"
  44. "yunion.io/x/onecloud/pkg/monitor/options"
  45. "yunion.io/x/onecloud/pkg/monitor/validators"
  46. "yunion.io/x/onecloud/pkg/util/logclient"
  47. "yunion.io/x/onecloud/pkg/util/stringutils2"
  48. )
  49. const (
  50. CommonAlertMetadataAlertType = "alert_type"
  51. CommonAlertMetadataFieldOpt = "field_opt"
  52. CommonAlertMetadataPointStr = "point_str"
  53. CommonAlertMetadataName = "meta_name"
  54. COMPANY_COPYRIGHT_ONECLOUD = "云联"
  55. BRAND_ONECLOUD_NAME_CN = "云联壹云"
  56. BRAND_ONECLOUD_NAME_EN = "YunionCloud"
  57. )
  58. var (
  59. CommonAlertManager *SCommonAlertManager
  60. )
  61. func init() {
  62. GetCommonAlertManager()
  63. //registry.RegisterService(CommonAlertManager)
  64. }
  65. func GetCommonAlertManager() *SCommonAlertManager {
  66. if CommonAlertManager != nil {
  67. return CommonAlertManager
  68. }
  69. CommonAlertManager = &SCommonAlertManager{
  70. SAlertManager: *NewAlertManager(SCommonAlert{}, "commonalert", "commonalerts"),
  71. }
  72. CommonAlertManager.SetVirtualObject(CommonAlertManager)
  73. return CommonAlertManager
  74. }
  75. type ISubscriptionManager interface {
  76. SetAlert(alert *SCommonAlert)
  77. DeleteAlert(alert *SCommonAlert)
  78. }
  79. // +onecloud:swagger-gen-model-singular=commonalert
  80. // +onecloud:swagger-gen-model-plural=commonalerts
  81. type SCommonAlertManager struct {
  82. SAlertManager
  83. subscriptionManager ISubscriptionManager
  84. }
  85. type ICommonAlert interface {
  86. db.IModel
  87. RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error
  88. DeleteAttachAlertRecords(ctx context.Context, userCred mcclient.TokenCredential) []error
  89. DetachAlertResourceOnDisable(ctx context.Context, userCred mcclient.TokenCredential) []error
  90. DetachMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error
  91. UpdateMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error
  92. GetMoreDetails(ctx context.Context, out monitor.CommonAlertDetails) (monitor.CommonAlertDetails, error)
  93. }
  94. type SCommonAlert struct {
  95. SAlert
  96. }
  97. func (man *SCommonAlertManager) SetSubscriptionManager(sman ISubscriptionManager) {
  98. man.subscriptionManager = sman
  99. }
  100. func (man *SCommonAlertManager) SetSubscriptionAlert(alert *SCommonAlert) {
  101. man.subscriptionManager.SetAlert(alert)
  102. }
  103. func (man *SCommonAlertManager) DeleteSubscriptionAlert(alert *SCommonAlert) {
  104. man.subscriptionManager.DeleteAlert(alert)
  105. }
  106. func (man *SCommonAlertManager) NamespaceScope() rbacscope.TRbacScope {
  107. return rbacscope.ScopeSystem
  108. }
  109. func (manager *SCommonAlertManager) Init() error {
  110. return nil
  111. }
  112. func (man *SCommonAlertManager) Run(ctx context.Context) error {
  113. alerts, err := man.GetAlerts(monitor.CommonAlertListInput{})
  114. if err != nil {
  115. return err
  116. }
  117. errs := make([]error, 0)
  118. for _, alert := range alerts {
  119. err := alert.UpdateMonitorResourceJoint(ctx, auth.AdminCredential())
  120. if err != nil {
  121. errs = append(errs, err)
  122. }
  123. }
  124. return errors.NewAggregate(errs)
  125. }
  126. func (man *SCommonAlertManager) UpdateAlertsResType(ctx context.Context, userCred mcclient.TokenCredential) error {
  127. alerts, err := man.GetAlerts(monitor.CommonAlertListInput{})
  128. if err != nil {
  129. return err
  130. }
  131. errs := make([]error, 0)
  132. for _, alert := range alerts {
  133. err := alert.UpdateResType()
  134. if err != nil {
  135. errs = append(errs, err)
  136. }
  137. }
  138. return errors.NewAggregate(errs)
  139. }
  140. func (man *SCommonAlertManager) validateRoles(ctx context.Context, roles []string) ([]string, error) {
  141. ids := []string{}
  142. for _, role := range roles {
  143. roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, role)
  144. if err != nil {
  145. return nil, errors.Wrapf(err, "fetch role by id or name: %q", role)
  146. }
  147. ids = append(ids, roleCache.GetId())
  148. }
  149. return ids, nil
  150. }
  151. func (man *SCommonAlertManager) ValidateCreateData(
  152. ctx context.Context, userCred mcclient.TokenCredential,
  153. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
  154. data monitor.CommonAlertCreateInput) (monitor.CommonAlertCreateInput, error) {
  155. if data.Period == "" {
  156. data.Period = "5m"
  157. }
  158. if data.AlertDuration == 0 {
  159. data.AlertDuration = 1
  160. }
  161. if data.Name == "" {
  162. return data, merrors.NewArgIsEmptyErr("name")
  163. }
  164. if data.Level == "" {
  165. data.Level = monitor.CommonAlertLevelNormal
  166. }
  167. if len(data.Channel) == 0 {
  168. data.Channel = []string{monitor.DEFAULT_SEND_NOTIFY_CHANNEL}
  169. }
  170. //else {
  171. // data.Channel = append(data.Channel, monitor.DEFAULT_SEND_NOTIFY_CHANNEL)
  172. //}
  173. if !utils.IsInStringArray(data.Level, monitor.CommonAlertLevels) {
  174. return data, httperrors.NewInputParameterError("Invalid level format: %s", data.Level)
  175. }
  176. if _, err := time.ParseDuration(data.Period); err != nil {
  177. return data, httperrors.NewInputParameterError("Invalid period format: %s", data.Period)
  178. }
  179. if data.SilentPeriod != "" {
  180. if _, err := time.ParseDuration(data.SilentPeriod); err != nil {
  181. return data, httperrors.NewInputParameterError("Invalid silent_period format: %s", data.SilentPeriod)
  182. }
  183. }
  184. // 默认的系统配置Recipients=commonalert-default
  185. if !utils.IsInStringArray(data.AlertType, []string{
  186. monitor.CommonAlertSystemAlertType,
  187. monitor.CommonAlertServiceAlertType,
  188. }) {
  189. if len(data.Recipients) == 0 && len(data.RobotIds) == 0 && len(data.Roles) == 0 {
  190. return data, merrors.NewArgIsEmptyErr("recipients, robot_ids or roles")
  191. }
  192. }
  193. if len(data.CommonMetricInputQuery.MetricQuery) == 0 {
  194. return data, merrors.NewArgIsEmptyErr("metric_query")
  195. } else {
  196. for _, query := range data.CommonMetricInputQuery.MetricQuery {
  197. if err := validateCommonAlertQuery(query); err != nil {
  198. return data, err
  199. }
  200. if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok {
  201. return data, httperrors.NewInputParameterError("the reduce is illegal: %s", query.Reduce)
  202. }
  203. /*if query.Threshold == 0 {
  204. return data, httperrors.NewInputParameterError("threshold is meaningless")
  205. }*/
  206. if strings.Contains(query.From, "now-") || strings.Contains(query.To, "now") {
  207. query.To = "now"
  208. query.From = "1h"
  209. }
  210. }
  211. }
  212. if data.AlertType != "" {
  213. if !utils.IsInStringArray(data.AlertType, validators.CommonAlertType) {
  214. return data, httperrors.NewInputParameterError("Invalid AlertType: %s", data.AlertType)
  215. }
  216. }
  217. var err = man.ValidateMetricQuery(&data.CommonMetricInputQuery, data.Scope, ownerId, true)
  218. if err != nil {
  219. return data, errors.Wrap(err, "metric query error")
  220. }
  221. // validate role
  222. if len(data.Roles) != 0 {
  223. roleIds, err := man.validateRoles(ctx, data.Roles)
  224. if err != nil {
  225. return data, errors.Wrap(err, "validateRole")
  226. }
  227. data.Roles = roleIds
  228. if !utils.IsInStringArray(data.Scope, []string{
  229. notiapi.SUBSCRIBER_SCOPE_SYSTEM,
  230. notiapi.SUBSCRIBER_SCOPE_DOMAIN,
  231. notiapi.SUBSCRIBER_SCOPE_PROJECT,
  232. }) {
  233. return data, httperrors.NewInputParameterError("unsupport scope %s", data.Scope)
  234. }
  235. }
  236. name, err := man.genName(ctx, ownerId, data.Name)
  237. if err != nil {
  238. return data, err
  239. }
  240. data.Name = name
  241. alertCreateInput, err := man.toAlertCreatInput(data)
  242. if err != nil {
  243. return data, errors.Wrap(err, "to alert creation input")
  244. }
  245. alertCreateInput, err = AlertManager.ValidateCreateData(ctx, userCred, ownerId, query, alertCreateInput)
  246. if err != nil {
  247. return data, err
  248. }
  249. data.AlertCreateInput = alertCreateInput
  250. return data, nil
  251. }
  252. func (man *SCommonAlertManager) genName(ctx context.Context, ownerId mcclient.IIdentityProvider, name string) (string,
  253. error) {
  254. lockman.LockRawObject(ctx, man.Keyword(), "name")
  255. defer lockman.ReleaseRawObject(ctx, man.Keyword(), "name")
  256. name, err := db.GenerateName(ctx, man, ownerId, name)
  257. if err != nil {
  258. return "", err
  259. }
  260. return name, nil
  261. }
  262. func (man *SCommonAlertManager) ValidateMetricQuery(metricRequest *monitor.CommonMetricInputQuery, scope string, ownerId mcclient.IIdentityProvider, isAlert bool) error {
  263. for _, q := range metricRequest.MetricQuery {
  264. metriInputQuery := monitor.MetricQueryInput{
  265. From: metricRequest.From,
  266. To: metricRequest.To,
  267. Interval: metricRequest.Interval,
  268. }
  269. setDefaultValue(q.AlertQuery, &metriInputQuery, scope, ownerId, isAlert)
  270. err := UnifiedMonitorManager.ValidateInputQuery(q.AlertQuery, &metriInputQuery)
  271. if err != nil {
  272. return err
  273. }
  274. }
  275. return nil
  276. }
  277. func (alert *SCommonAlert) setAlertType(ctx context.Context, userCred mcclient.TokenCredential, alertType string) error {
  278. return alert.SetMetadata(ctx, CommonAlertMetadataAlertType, alertType, userCred)
  279. }
  280. func (alert *SCommonAlert) getAlertType() string {
  281. ret := alert.GetMetadata(context.Background(), CommonAlertMetadataAlertType, nil)
  282. return ret
  283. }
  284. func (alert *SCommonAlert) setFieldOpt(ctx context.Context, userCred mcclient.TokenCredential, fieldOpt string) error {
  285. return alert.SetMetadata(ctx, CommonAlertMetadataFieldOpt, fieldOpt, userCred)
  286. }
  287. func (alert *SCommonAlert) getFieldOpt() string {
  288. return alert.GetMetadata(context.Background(), CommonAlertMetadataFieldOpt, nil)
  289. }
  290. func (alert *SCommonAlert) setPointStr(ctx context.Context, userCred mcclient.TokenCredential, fieldOpt string) error {
  291. return alert.SetMetadata(ctx, CommonAlertMetadataPointStr, fieldOpt, userCred)
  292. }
  293. func (alert *SCommonAlert) getPointStr() string {
  294. return alert.GetMetadata(context.Background(), CommonAlertMetadataPointStr, nil)
  295. }
  296. func (alert *SCommonAlert) setMetaName(ctx context.Context, userCred mcclient.TokenCredential, metaName string) error {
  297. return alert.SetMetadata(ctx, CommonAlertMetadataName, metaName, userCred)
  298. }
  299. func (alert *SCommonAlert) getMetaName() string {
  300. return alert.GetMetadata(context.Background(), CommonAlertMetadataName, nil)
  301. }
  302. func (alert *SCommonAlert) CustomizeCreate(
  303. ctx context.Context, userCred mcclient.TokenCredential,
  304. ownerId mcclient.IIdentityProvider,
  305. query jsonutils.JSONObject,
  306. data jsonutils.JSONObject,
  307. ) error {
  308. err := alert.SMonitorScopedResource.CustomizeCreate(ctx, userCred, ownerId, query, data)
  309. if err != nil {
  310. return err
  311. }
  312. alert.State = string(monitor.AlertStateUnknown)
  313. alert.LastStateChange = time.Now()
  314. input := new(monitor.CommonAlertCreateInput)
  315. if err := data.Unmarshal(input); err != nil {
  316. return err
  317. }
  318. if input.DisableNotifyRecovery != nil {
  319. alert.DisableNotifyRecovery = *input.DisableNotifyRecovery
  320. }
  321. return alert.customizeCreateNotis(ctx, userCred, query, data)
  322. }
  323. func (alert *SCommonAlert) customizeCreateNotis(ctx context.Context, userCred mcclient.TokenCredential,
  324. query jsonutils.JSONObject,
  325. data jsonutils.JSONObject) error {
  326. input := new(monitor.CommonAlertCreateInput)
  327. if err := data.Unmarshal(input); err != nil {
  328. return err
  329. }
  330. if input.AlertType == monitor.CommonAlertSystemAlertType {
  331. s := &monitor.NotificationSettingOneCloud{
  332. Channel: "webconsole",
  333. }
  334. return alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, true)
  335. }
  336. alert.SetChannel(ctx, input.Channel)
  337. for _, channel := range input.Channel {
  338. s := &monitor.NotificationSettingOneCloud{
  339. Channel: channel,
  340. UserIds: input.Recipients,
  341. }
  342. if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
  343. return errors.Wrap(err, fmt.Sprintf("create notify[channel is %s] error", channel))
  344. }
  345. }
  346. if len(input.RobotIds) != 0 {
  347. s := &monitor.NotificationSettingOneCloud{
  348. Channel: string(notify.NotifyByRobot),
  349. RobotIds: input.RobotIds,
  350. }
  351. if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
  352. return errors.Wrapf(err, "create alert notification by robot %v", input.RobotIds)
  353. }
  354. }
  355. if len(input.Roles) != 0 {
  356. s := &monitor.NotificationSettingOneCloud{
  357. RoleIds: input.Roles,
  358. }
  359. if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
  360. return errors.Wrapf(err, "create alert notify by role %v, scope %q", input.Roles, input.Scope)
  361. }
  362. }
  363. return nil
  364. }
  365. func (alert *SCommonAlert) createAlertNoti(ctx context.Context, userCred mcclient.TokenCredential, notiName string, settings *monitor.NotificationSettingOneCloud, silentPeriod string, isSysNoti bool) error {
  366. noti, err := NotificationManager.CreateOneCloudNotification(ctx, userCred, notiName, settings, silentPeriod)
  367. if err != nil {
  368. return errors.Wrap(err, "create notification")
  369. }
  370. if isSysNoti {
  371. _, err = db.Update(noti, func() error {
  372. //对于默认系统报警,isDefault=true
  373. noti.IsDefault = true
  374. return nil
  375. })
  376. if err != nil {
  377. return errors.Wrap(err, "create notification")
  378. }
  379. }
  380. if alert.Id == "" {
  381. alert.Id = db.DefaultUUIDGenerator()
  382. }
  383. _, err = alert.AttachNotification(
  384. ctx, userCred, noti,
  385. monitor.AlertNotificationStateUnknown,
  386. "")
  387. return err
  388. }
  389. func (alert *SCommonAlert) PostCreate(ctx context.Context,
  390. userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
  391. query jsonutils.JSONObject, data jsonutils.JSONObject) {
  392. input := new(monitor.CommonAlertCreateInput)
  393. if err := data.Unmarshal(input); err != nil {
  394. log.Errorf("post create unmarshal input: %v", err)
  395. return
  396. }
  397. alert.SetStatus(ctx, userCred, monitor.ALERT_STATUS_READY, "")
  398. if input.AlertType != "" {
  399. if err := alert.setAlertType(ctx, userCred, input.AlertType); err != nil {
  400. log.Errorf("etAlertType error for %q: %v", alert.GetId(), err)
  401. }
  402. }
  403. fieldOpt := ""
  404. for i, metricQ := range input.CommonMetricInputQuery.MetricQuery {
  405. if metricQ.FieldOpt != "" {
  406. if i == 0 {
  407. fieldOpt = string(metricQ.FieldOpt)
  408. continue
  409. }
  410. fieldOpt = fmt.Sprintf("%s+%s", fieldOpt, metricQ.FieldOpt)
  411. }
  412. }
  413. if fieldOpt != "" {
  414. alert.setFieldOpt(ctx, userCred, fieldOpt)
  415. }
  416. if input.GetPointStr {
  417. alert.setPointStr(ctx, userCred, strconv.FormatBool(input.GetPointStr))
  418. }
  419. if len(input.MetaName) != 0 {
  420. alert.setMetaName(ctx, userCred, input.MetaName)
  421. }
  422. _, err := alert.PerformSetScope(ctx, userCred, query, data)
  423. if err != nil {
  424. log.Errorln(errors.Wrap(err, "Alert PerformSetScope"))
  425. }
  426. CommonAlertManager.SetSubscriptionAlert(alert)
  427. //alert.StartUpdateMonitorAlertJointTask(ctx, userCred)
  428. }
  429. func (man *SCommonAlertManager) ListItemFilter(
  430. ctx context.Context, q *sqlchemy.SQuery,
  431. userCred mcclient.TokenCredential,
  432. query monitor.CommonAlertListInput,
  433. ) (*sqlchemy.SQuery, error) {
  434. // 如果指定了时间段和 top 参数,执行特殊的 top 查询
  435. if query.Top != nil {
  436. return man.getTopAlertsByResourceCount(ctx, q, userCred, query)
  437. }
  438. q, err := man.SAlertManager.ListItemFilter(ctx, q, userCred, query.AlertListInput)
  439. if err != nil {
  440. return nil, err
  441. }
  442. man.FieldListFilter(q, query)
  443. return q, nil
  444. }
  445. func (man *SCommonAlertManager) FieldListFilter(q *sqlchemy.SQuery, input monitor.CommonAlertListInput) {
  446. // if len(input.UsedBy) == 0 {
  447. // q.Filter(sqlchemy.IsNull(q.Field("used_by")))
  448. // } else {
  449. // q.Equals("used_by", input.UsedBy)
  450. // }
  451. if len(input.UsedBy) != 0 {
  452. q.Equals("used_by", input.UsedBy)
  453. }
  454. if len(input.Level) > 0 {
  455. q.Equals("level", input.Level)
  456. }
  457. if len(input.Name) != 0 {
  458. q.Contains("name", input.Name)
  459. }
  460. }
  461. // getTopAlertsByResourceCount 查询指定时间段内报警资源最多的 top N 监控策略
  462. func (man *SCommonAlertManager) getTopAlertsByResourceCount(
  463. ctx context.Context,
  464. q *sqlchemy.SQuery,
  465. userCred mcclient.TokenCredential,
  466. query monitor.CommonAlertListInput,
  467. ) (*sqlchemy.SQuery, error) {
  468. // 验证时间段和 top 参数
  469. startTime, endTime, top, err := validateTopQueryInput(query.TopQueryInput)
  470. if err != nil {
  471. return nil, err
  472. }
  473. // 查询指定时间段内的 AlertRecord
  474. recordQuery := AlertRecordManager.Query("alert_id", "res_ids")
  475. recordQuery = recordQuery.GE("created_at", startTime).LE("created_at", endTime)
  476. recordQuery = recordQuery.IsNotNull("res_type").IsNotEmpty("res_type")
  477. recordQuery = recordQuery.IsNotEmpty("res_ids")
  478. // 应用权限过滤
  479. recordQuery, err = AlertRecordManager.SScopedResourceBaseManager.ListItemFilter(
  480. ctx, recordQuery, userCred, query.ScopedResourceBaseListInput)
  481. if err != nil {
  482. return nil, errors.Wrap(err, "AlertRecordManager.ListItemFilter")
  483. }
  484. // 执行查询获取所有记录
  485. type RecordRow struct {
  486. AlertId string
  487. ResIds string
  488. }
  489. rows := make([]RecordRow, 0)
  490. err = recordQuery.All(&rows)
  491. if err != nil {
  492. return nil, errors.Wrap(err, "query alert records")
  493. }
  494. // 统计每个 alert_id 的唯一资源数量
  495. alertResourceCount := make(map[string]sets.String)
  496. for _, row := range rows {
  497. if len(row.ResIds) == 0 {
  498. continue
  499. }
  500. // 解析 res_ids(逗号分隔)
  501. resIds := strings.Split(row.ResIds, ",")
  502. if alertResourceCount[row.AlertId] == nil {
  503. alertResourceCount[row.AlertId] = sets.NewString()
  504. }
  505. for _, resId := range resIds {
  506. resId = strings.TrimSpace(resId)
  507. if len(resId) > 0 {
  508. alertResourceCount[row.AlertId].Insert(resId)
  509. }
  510. }
  511. }
  512. // 转换为切片并按资源数量排序
  513. type AlertCount struct {
  514. AlertId string
  515. Count int
  516. }
  517. alertCounts := make([]AlertCount, 0, len(alertResourceCount))
  518. for alertId, resSet := range alertResourceCount {
  519. alertCounts = append(alertCounts, AlertCount{
  520. AlertId: alertId,
  521. Count: resSet.Len(),
  522. })
  523. }
  524. // 按资源数量降序排序
  525. for i := 0; i < len(alertCounts)-1; i++ {
  526. for j := i + 1; j < len(alertCounts); j++ {
  527. if alertCounts[i].Count < alertCounts[j].Count {
  528. alertCounts[i], alertCounts[j] = alertCounts[j], alertCounts[i]
  529. }
  530. }
  531. }
  532. // 获取 top N 的 alert_id
  533. topAlertIds := make([]string, 0, top)
  534. for i := 0; i < top && i < len(alertCounts); i++ {
  535. topAlertIds = append(topAlertIds, alertCounts[i].AlertId)
  536. }
  537. if len(topAlertIds) == 0 {
  538. // 如果没有找到任何记录,返回空查询
  539. return q.FilterByFalse(), nil
  540. }
  541. // 用 top alert_id 过滤 CommonAlert 查询
  542. q, err = man.SAlertManager.ListItemFilter(ctx, q, userCred, query.AlertListInput)
  543. if err != nil {
  544. return nil, err
  545. }
  546. man.FieldListFilter(q, query)
  547. q = q.In("id", topAlertIds)
  548. return q, nil
  549. }
  550. func (manager *SCommonAlertManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
  551. res := manager.SResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
  552. if keys.Contains("tenant") {
  553. if projectId, ok := rowMap["tenant_id"]; ok && projectId != "" {
  554. tenant, err := db.TenantCacheManager.FetchTenantById(ctx, projectId)
  555. if err == nil {
  556. res.Set("tenant", jsonutils.NewString(fmt.Sprintf("%s/%s", tenant.GetName(),
  557. tenant.GetProjectDomain())))
  558. }
  559. } else {
  560. tenant, err := db.TenantCacheManager.FetchDomainById(ctx, rowMap["domain_id"])
  561. if err == nil {
  562. dictionaryVal := GetGlobalSettingsDictionary(ctx, "domain")
  563. res.Set("tenant", jsonutils.NewString(fmt.Sprintf("%s%s", tenant.GetProjectDomain(), dictionaryVal)))
  564. }
  565. }
  566. }
  567. return res
  568. }
  569. func GetGlobalSettingsDictionary(ctx context.Context, param string) (val string) {
  570. s := auth.GetAdminSession(ctx, "")
  571. globalSettings, err := yunionconf.Parameters.GetGlobalSettings(s, jsonutils.NewDict())
  572. if err != nil {
  573. log.Errorf("GetGlobalSettings err:%v", err)
  574. return
  575. }
  576. dictionary, err := globalSettings.Get("value", "dictionary")
  577. if err != nil {
  578. log.Errorf("can not get dictionary:%s", globalSettings.String())
  579. return
  580. }
  581. lang := appctx.Lang(ctx)
  582. switch lang {
  583. case language.English:
  584. val, _ = dictionary.GetString("en", param)
  585. default:
  586. val, _ = dictionary.GetString("zh", param)
  587. }
  588. return
  589. }
  590. func (man *SCommonAlertManager) CustomizeFilterList(
  591. ctx context.Context, q *sqlchemy.SQuery,
  592. userCred mcclient.TokenCredential, query jsonutils.JSONObject) (
  593. *db.CustomizeListFilters, error) {
  594. filters, err := man.SAlertManager.CustomizeFilterList(ctx, q, userCred, query)
  595. if err != nil {
  596. return nil, err
  597. }
  598. input := new(monitor.CommonAlertListInput)
  599. if err := query.Unmarshal(input); err != nil {
  600. return nil, err
  601. }
  602. wrapF := func(f func(obj *SCommonAlert) (bool, error)) func(object jsonutils.JSONObject) (bool, error) {
  603. return func(data jsonutils.JSONObject) (bool, error) {
  604. id, err := data.GetString("id")
  605. if err != nil {
  606. return true, nil
  607. }
  608. obj, err := man.GetAlert(id)
  609. if err != nil {
  610. return false, err
  611. }
  612. return f(obj)
  613. }
  614. }
  615. if input.Metric != "" {
  616. metric := input.Metric
  617. meaurement, field, err := GetMeasurementField(metric)
  618. if err != nil {
  619. return nil, err
  620. }
  621. mF := func(obj *SCommonAlert) (bool, error) {
  622. settings := obj.Settings
  623. for _, s := range settings.Conditions {
  624. if s.Query.Model.Measurement == meaurement && len(s.Query.Model.Selects) == 1 {
  625. if IsQuerySelectHasField(s.Query.Model.Selects[0], field) {
  626. return true, nil
  627. }
  628. }
  629. }
  630. return false, nil
  631. }
  632. filters.Append(wrapF(mF))
  633. }
  634. if input.AlertType != "" {
  635. filters.Append(wrapF(func(obj *SCommonAlert) (bool, error) {
  636. return obj.getAlertType() == input.AlertType, nil
  637. }))
  638. }
  639. if len(input.ResType) != 0 {
  640. mF := func(obj *SCommonAlert) (bool, error) {
  641. settings := obj.Settings
  642. for _, s := range settings.Conditions {
  643. if mesurement, contain := MetricMeasurementManager.measurementsCache.Get(s.Query.Model.
  644. Measurement); contain {
  645. if utils.IsInStringArray(mesurement.ResType, input.ResType) {
  646. return true, nil
  647. }
  648. }
  649. }
  650. return false, nil
  651. }
  652. filters.Append(wrapF(mF))
  653. }
  654. return filters, nil
  655. }
  656. func (man *SCommonAlertManager) GetAlert(id string) (*SCommonAlert, error) {
  657. obj, err := man.FetchById(id)
  658. if err != nil {
  659. return nil, err
  660. }
  661. return obj.(*SCommonAlert), nil
  662. }
  663. func (manager *SCommonAlertManager) GetAlerts(input monitor.CommonAlertListInput) ([]SCommonAlert, error) {
  664. alerts := make([]SCommonAlert, 0)
  665. query := manager.Query()
  666. manager.FieldListFilter(query, input)
  667. err := db.FetchModelObjects(manager, query, &alerts)
  668. if err != nil {
  669. return nil, errors.Wrap(err, "SCommonAlertManagerGetAlerts err")
  670. }
  671. return alerts, nil
  672. }
  673. func (man *SCommonAlertManager) FetchCustomizeColumns(
  674. ctx context.Context,
  675. userCred mcclient.TokenCredential,
  676. query jsonutils.JSONObject,
  677. objs []interface{},
  678. fields stringutils2.SSortedStrings,
  679. isList bool,
  680. ) []monitor.CommonAlertDetails {
  681. rows := make([]monitor.CommonAlertDetails, len(objs))
  682. alertRows := man.SAlertManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  683. alertIds := make([]string, len(objs))
  684. for i := range rows {
  685. rows[i].AlertDetails = alertRows[i]
  686. alert := objs[i].(*SCommonAlert)
  687. alertIds[i] = alert.Id
  688. }
  689. ans := []SAlertnotification{}
  690. err := AlertNotificationManager.Query().In("alert_id", alertIds).Desc("index").All(&ans)
  691. if err != nil {
  692. log.Errorf("q.All")
  693. return rows
  694. }
  695. alertNotificationMap := make(map[string][]SAlertnotification)
  696. notificateIds := []string{}
  697. for i := range ans {
  698. _, ok := alertNotificationMap[ans[i].AlertId]
  699. if !ok {
  700. alertNotificationMap[ans[i].AlertId] = []SAlertnotification{}
  701. }
  702. notificateIds = append(notificateIds, ans[i].NotificationId)
  703. alertNotificationMap[ans[i].AlertId] = append(alertNotificationMap[ans[i].AlertId], ans[i])
  704. }
  705. notis := make(map[string]SNotification)
  706. err = db.FetchModelObjectsByIds(NotificationManager, "id", notificateIds, notis)
  707. if err != nil {
  708. log.Errorf("db.FetchModelObjectsByIds err:%v", err)
  709. return rows
  710. }
  711. metas := []db.SMetadata{}
  712. err = db.Metadata.Query().In("obj_id", alertIds).Equals("obj_type", man.Keyword()).Equals("key", CommonAlertMetadataAlertType).All(&metas)
  713. if err != nil {
  714. log.Errorf("db.MetadataManager.Query err:%v", err)
  715. return rows
  716. }
  717. metaMap := make(map[string]string)
  718. for i := range metas {
  719. metaMap[metas[i].ObjId] = metas[i].Value
  720. }
  721. for i := range rows {
  722. alert := objs[i].(*SCommonAlert)
  723. rows[i].DisableNotifyRecovery = alert.DisableNotifyRecovery
  724. if alertNotis, ok := alertNotificationMap[alertIds[i]]; ok {
  725. channel := sets.String{}
  726. for j, alertNoti := range alertNotis {
  727. noti, ok := notis[alertNoti.NotificationId]
  728. if !ok {
  729. continue
  730. }
  731. settings := new(monitor.NotificationSettingOneCloud)
  732. if err := noti.Settings.Unmarshal(settings); err != nil {
  733. log.Errorf("Unmarshal to NotificationSettingOneCloud err:%v", err)
  734. return rows
  735. }
  736. if j == 0 {
  737. rows[i].Recipients = settings.UserIds
  738. }
  739. if !utils.IsInStringArray(settings.Channel,
  740. []string{monitor.DEFAULT_SEND_NOTIFY_CHANNEL, string(notify.NotifyByRobot)}) {
  741. channel.Insert(settings.Channel)
  742. }
  743. if noti.Frequency != 0 {
  744. rows[i].SilentPeriod = fmt.Sprintf("%dm", noti.Frequency/60)
  745. }
  746. if len(settings.RobotIds) != 0 {
  747. rows[i].RobotIds = settings.RobotIds
  748. }
  749. if len(settings.RoleIds) != 0 {
  750. rows[i].RoleIds = settings.RoleIds
  751. }
  752. }
  753. rows[i].Channel = channel.List()
  754. }
  755. rows[i].AlertType = metaMap[alertIds[i]]
  756. if alert.Frequency < 60 {
  757. rows[i].Period = fmt.Sprintf("%ds", alert.Frequency)
  758. } else {
  759. rows[i].Period = fmt.Sprintf("%dm", alert.Frequency/60)
  760. }
  761. rows[i].AlertDuration = alert.For / alert.Frequency
  762. if rows[i].AlertDuration == 0 {
  763. rows[i].AlertDuration = 1
  764. }
  765. err := alert.getCommonAlertMetricDetails(&rows[i])
  766. if err != nil {
  767. log.Errorf("getCommonAlertMetricDetails err:%v", err)
  768. return rows
  769. }
  770. }
  771. return rows
  772. }
  773. func (alert *SCommonAlert) ValidateDeleteCondition(ctx context.Context, info *monitor.CommonAlertDetails) error {
  774. if gotypes.IsNil(info) {
  775. info = &monitor.CommonAlertDetails{}
  776. }
  777. if info.AlertType == monitor.CommonAlertSystemAlertType {
  778. return httperrors.NewInputParameterError("Cannot delete system alert")
  779. }
  780. return nil
  781. }
  782. func (alert *SCommonAlert) GetMoreDetails(ctx context.Context, out monitor.CommonAlertDetails) (monitor.CommonAlertDetails, error) {
  783. s := auth.GetAdminSession(ctx, options.Options.Region)
  784. token := s.GetToken()
  785. ret := CommonAlertManager.FetchCustomizeColumns(ctx, token, jsonutils.NewDict(), []interface{}{alert}, stringutils2.SSortedStrings{}, false)
  786. if len(ret) != 1 {
  787. return out, errors.Wrapf(cloudprovider.ErrNotFound, "FetchCustomizeColumns")
  788. }
  789. return ret[0], nil
  790. }
  791. func (alert *SCommonAlert) getCommonAlertMetricDetails(out *monitor.CommonAlertDetails) error {
  792. metricDetails, err := alert.GetCommonAlertMetricDetails()
  793. if err != nil {
  794. return err
  795. }
  796. out.CommonAlertMetricDetails = metricDetails
  797. return nil
  798. }
  799. func (alert *SCommonAlert) GetCommonAlertMetricDetails() ([]*monitor.CommonAlertMetricDetails, error) {
  800. setting, err := alert.GetSettings()
  801. if err != nil {
  802. return nil, errors.Wrap(err, "get alert settings")
  803. }
  804. if len(setting.Conditions) == 0 {
  805. return nil, nil
  806. }
  807. ret := make([]*monitor.CommonAlertMetricDetails, len(setting.Conditions))
  808. for i, cond := range setting.Conditions {
  809. metricDetails := alert.GetCommonAlertMetricDetailsFromAlertCondition(i, &cond)
  810. ret[i] = metricDetails
  811. setting.Conditions[i] = cond
  812. }
  813. // side effect, update setting cause of setting.Conditions has changed by GetCommonAlertMetricDetailsFromAlertCondition
  814. alert.Settings = setting
  815. return ret, nil
  816. }
  817. func (alert *SCommonAlert) GetCommonAlertMetricDetailsFromAlertCondition(index int, cond *monitor.AlertCondition) *monitor.CommonAlertMetricDetails {
  818. fieldOpt := alert.getFieldOpt()
  819. metricDetails := new(monitor.CommonAlertMetricDetails)
  820. if fieldOpt != "" {
  821. metricDetails.FieldOpt = strings.Split(fieldOpt, "+")[index]
  822. }
  823. poinStr := alert.getPointStr()
  824. if len(poinStr) != 0 {
  825. bl, _ := strconv.ParseBool(poinStr)
  826. metricDetails.GetPointStr = bl
  827. }
  828. getCommonAlertMetricDetailsFromCondition(cond, metricDetails)
  829. return metricDetails
  830. }
  831. func getCommonAlertMetricDetailsFromCondition(
  832. cond *monitor.AlertCondition,
  833. metricDetails *monitor.CommonAlertMetricDetails,
  834. ) {
  835. cmp := ""
  836. switch cond.Evaluator.Type {
  837. case "gt":
  838. cmp = ">"
  839. case "eq":
  840. cmp = "=="
  841. case "lt":
  842. cmp = "<"
  843. case "within_range":
  844. cmp = "within_range"
  845. case "outside_range":
  846. cmp = "outside_range"
  847. }
  848. metricDetails.Comparator = cmp
  849. // 处理 ranged types
  850. if utils.IsInStringArray(cond.Evaluator.Type, validators.EvaluatorRangedTypes) {
  851. if len(cond.Evaluator.Params) >= 2 {
  852. metricDetails.ThresholdRange = []float64{cond.Evaluator.Params[0], cond.Evaluator.Params[1]}
  853. }
  854. } else {
  855. // 处理默认 types
  856. if len(cond.Evaluator.Params) != 0 {
  857. metricDetails.Threshold = cond.Evaluator.Params[0]
  858. }
  859. }
  860. metricDetails.Reduce = cond.Reducer.Type
  861. metricDetails.ConditionType = cond.Type
  862. if metricDetails.ConditionType == monitor.METRIC_QUERY_TYPE_NO_DATA {
  863. metricDetails.ThresholdStr = monitor.METRIC_QUERY_NO_DATA_THESHOLD
  864. metricDetails.Comparator = monitor.METRIC_QUERY_NO_DATA_THESHOLD
  865. }
  866. q := cond.Query
  867. measurement := q.Model.Measurement
  868. field := ""
  869. for i, sel := range q.Model.Selects {
  870. if i == 0 {
  871. field = sel[0].Params[0]
  872. continue
  873. }
  874. if metricDetails.FieldOpt != "" {
  875. field = fmt.Sprintf("%s%s%s", field, metricDetails.FieldOpt, sel[0].Params[0])
  876. }
  877. }
  878. //field := q.Model.Selects[0][0].Params[0]
  879. db := q.Model.Database
  880. var groupby string
  881. for _, grb := range q.Model.GroupBy {
  882. if grb.Type == "tag" {
  883. groupby = grb.Params[0]
  884. break
  885. }
  886. }
  887. cond.Query.Model.Tags = filterDefaultTags(q.Model.Tags)
  888. metricDetails.Measurement = measurement
  889. metricDetails.Field = field
  890. metricDetails.DB = db
  891. metricDetails.Groupby = groupby
  892. metricDetails.Filters = cond.Query.Model.Tags
  893. metricDetails.Operator = cond.Operator
  894. //fill measurement\field desciption info
  895. getMetricDescriptionDetails(metricDetails)
  896. }
  897. func filterDefaultTags(queryTag []monitor.MetricQueryTag) []monitor.MetricQueryTag {
  898. newQueryTags := make([]monitor.MetricQueryTag, 0)
  899. for i, tagFilter := range queryTag {
  900. if tagFilter.Key == "tenant_id" {
  901. continue
  902. }
  903. if tagFilter.Key == "domain_id" {
  904. continue
  905. }
  906. if tagFilter.Key == hostconsts.TELEGRAF_TAG_KEY_RES_TYPE {
  907. continue
  908. }
  909. newQueryTags = append(newQueryTags, queryTag[i])
  910. }
  911. return newQueryTags
  912. }
  913. func getMetricDescriptionDetails(metricDetails *monitor.CommonAlertMetricDetails) {
  914. influxdbMeasurements := DataSourceManager.getMetricDescriptions([]monitor.InfluxMeasurement{
  915. {Measurement: metricDetails.Measurement},
  916. })
  917. if len(influxdbMeasurements) == 0 {
  918. return
  919. }
  920. if len(influxdbMeasurements[0].MeasurementDisplayName) != 0 {
  921. metricDetails.MeasurementDisplayName = influxdbMeasurements[0].MeasurementDisplayName
  922. }
  923. if len(influxdbMeasurements[0].ResType) != 0 {
  924. metricDetails.ResType = influxdbMeasurements[0].ResType
  925. }
  926. fields := make([]string, 0)
  927. if len(metricDetails.FieldOpt) != 0 {
  928. fields = append(fields, strings.Split(metricDetails.Field, metricDetails.FieldOpt)...)
  929. } else {
  930. fields = append(fields, metricDetails.Field)
  931. }
  932. for _, field := range fields {
  933. if influxdbMeasurements[0].FieldDescriptions == nil {
  934. return
  935. }
  936. if fieldDes, ok := influxdbMeasurements[0].FieldDescriptions[field]; ok {
  937. metricDetails.FieldDescription = fieldDes
  938. if utils.IsInStringArray(metricDetails.FieldDescription.Unit, []string{
  939. monitor.METRIC_UNIT_COUNT,
  940. monitor.METRIC_UNIT_NULL,
  941. }) {
  942. metricDetails.FieldDescription.Unit = ""
  943. }
  944. if len(metricDetails.FieldOpt) != 0 {
  945. metricDetails.FieldDescription.Name = metricDetails.Field
  946. metricDetails.FieldDescription.DisplayName = metricDetails.Field
  947. getExtraFieldDetails(metricDetails)
  948. break
  949. }
  950. }
  951. }
  952. }
  953. func getExtraFieldDetails(metricDetails *monitor.CommonAlertMetricDetails) {
  954. if metricDetails.FieldOpt == string(monitor.CommonAlertFieldOptDivision) && metricDetails.Threshold < float64(1) {
  955. metricDetails.Threshold = metricDetails.Threshold * float64(100)
  956. metricDetails.FieldDescription.Unit = "%"
  957. }
  958. }
  959. func getQueryEvalType(evalType string) monitor.EvaluatorType {
  960. var typ monitor.EvaluatorType
  961. switch evalType {
  962. case ">=", ">":
  963. typ = monitor.EvaluatorTypeGT
  964. case "<=", "<":
  965. typ = monitor.EvaluatorTypeLT
  966. case "==":
  967. typ = monitor.EvaluatorTypeEQ
  968. case "within_range":
  969. typ = monitor.EvaluatorTypeWithinRange
  970. case "outside_range":
  971. typ = monitor.EvaluatorTypeOutsideRange
  972. }
  973. return typ
  974. }
  975. // validateCommonAlertQuery 校验 CommonAlertQuery 的 comparator 和 threshold_range
  976. func validateCommonAlertQuery(query *monitor.CommonAlertQuery) error {
  977. if query.ConditionType == monitor.METRIC_QUERY_TYPE_NO_DATA {
  978. query.Comparator = "=="
  979. }
  980. evalType := getQueryEvalType(query.Comparator)
  981. if !sets.NewString(append(
  982. validators.EvaluatorDefaultTypes,
  983. validators.EvaluatorRangedTypes...)...).Has(string(evalType)) {
  984. return httperrors.NewInputParameterError("the Comparator is illegal: %s", query.Comparator)
  985. }
  986. // 验证 ranged types 的参数
  987. if utils.IsInStringArray(string(evalType), validators.EvaluatorRangedTypes) {
  988. if len(query.ThresholdRange) < 2 {
  989. return httperrors.NewInputParameterError("threshold_range or outside_range requires 2 parameters, got %d", len(query.ThresholdRange))
  990. }
  991. // 确保第一项小于等于第二项
  992. if query.ThresholdRange[0] > query.ThresholdRange[1] {
  993. return httperrors.NewInputParameterError("threshold_range first value (%v) must be less than or equal to second value (%v)", query.ThresholdRange[0], query.ThresholdRange[1])
  994. }
  995. }
  996. return nil
  997. }
  998. // validateComparatorAndThreshold 校验字符串形式的 comparator, threshold 和 threshold_range
  999. func validateComparatorAndThreshold(comparator string, threshold string, thresholdRange []jsonutils.JSONObject) error {
  1000. var evalType monitor.EvaluatorType
  1001. if len(comparator) != 0 {
  1002. evalType = getQueryEvalType(comparator)
  1003. if !utils.IsInStringArray(string(evalType), append(validators.EvaluatorDefaultTypes, validators.EvaluatorRangedTypes...)) {
  1004. return httperrors.NewInputParameterError("the Comparator is illegal: %s", comparator)
  1005. }
  1006. // 验证 ranged types 的参数
  1007. if utils.IsInStringArray(string(evalType), validators.EvaluatorRangedTypes) {
  1008. if len(thresholdRange) < 2 {
  1009. return httperrors.NewInputParameterError("threshold_range or outside_range requires 2 parameters, got %d", len(thresholdRange))
  1010. }
  1011. }
  1012. }
  1013. if len(threshold) != 0 {
  1014. _, err := strconv.ParseFloat(threshold, 64)
  1015. if err != nil {
  1016. return httperrors.NewInputParameterError("threshold:%s should be number type", threshold)
  1017. }
  1018. }
  1019. if len(thresholdRange) > 0 {
  1020. if len(thresholdRange) < 2 {
  1021. return httperrors.NewInputParameterError("threshold_range requires 2 parameters, got %d", len(thresholdRange))
  1022. }
  1023. vals := make([]float64, len(thresholdRange))
  1024. for i, val := range thresholdRange {
  1025. parsedVal, err := strconv.ParseFloat(val.String(), 64)
  1026. if err != nil {
  1027. return httperrors.NewInputParameterError("threshold_range[%d]: %s should be number type", i, val.String())
  1028. }
  1029. vals[i] = parsedVal
  1030. }
  1031. // 确保第一项小于等于第二项
  1032. if vals[0] > vals[1] {
  1033. return httperrors.NewInputParameterError("threshold_range first value (%v) must be less than or equal to second value (%v)", vals[0], vals[1])
  1034. }
  1035. }
  1036. return nil
  1037. }
  1038. func (man *SCommonAlertManager) toAlertCreatInput(input monitor.CommonAlertCreateInput) (monitor.AlertCreateInput, error) {
  1039. freq, _ := time.ParseDuration(input.Period)
  1040. ret := new(monitor.AlertCreateInput)
  1041. ret.Name = input.Name
  1042. ret.Frequency = int64(freq / time.Second)
  1043. if input.AlertDuration != 1 {
  1044. ret.For = ret.Frequency * input.AlertDuration
  1045. }
  1046. ret.Level = input.Level
  1047. //ret.Settings =monitor.AlertSetting{}
  1048. for _, metricquery := range input.CommonMetricInputQuery.MetricQuery {
  1049. conditionType := "query"
  1050. if len(metricquery.ConditionType) != 0 {
  1051. conditionType = metricquery.ConditionType
  1052. }
  1053. evalType := getQueryEvalType(metricquery.Comparator)
  1054. var evaluatorParams []float64
  1055. // 处理 ranged types (within_range, outside_range)
  1056. if utils.IsInStringArray(string(evalType), validators.EvaluatorRangedTypes) {
  1057. if len(metricquery.ThresholdRange) < 2 {
  1058. return *ret, httperrors.NewInputParameterError("threshold_range or outside_range requires 2 parameters, got %d", len(metricquery.ThresholdRange))
  1059. }
  1060. fieldOpt := monitor.CommonAlertFieldOpt(metricquery.FieldOpt)
  1061. evaluatorParams = []float64{
  1062. fieldOperatorThreshold(fieldOpt, metricquery.ThresholdRange[0]),
  1063. fieldOperatorThreshold(fieldOpt, metricquery.ThresholdRange[1]),
  1064. }
  1065. } else {
  1066. // 处理默认 types (gt, lt, eq)
  1067. fieldOpt := monitor.CommonAlertFieldOpt(metricquery.FieldOpt)
  1068. evaluatorParams = []float64{fieldOperatorThreshold(fieldOpt, metricquery.Threshold)}
  1069. }
  1070. condition := monitor.AlertCondition{
  1071. Type: conditionType,
  1072. Query: *metricquery.AlertQuery,
  1073. Reducer: monitor.Condition{Type: metricquery.Reduce},
  1074. Evaluator: monitor.Condition{Type: string(evalType), Params: evaluatorParams},
  1075. Operator: "and",
  1076. }
  1077. if metricquery.Operator != "" {
  1078. if !sets.NewString("and", "or").Has(metricquery.Operator) {
  1079. return *ret, httperrors.NewInputParameterError("invalid operator %s", metricquery.Operator)
  1080. }
  1081. condition.Operator = metricquery.Operator
  1082. }
  1083. if metricquery.FieldOpt != "" {
  1084. condition.Reducer.Operators = []string{string(metricquery.FieldOpt)}
  1085. }
  1086. ret.Settings.Conditions = append(ret.Settings.Conditions, condition)
  1087. }
  1088. return *ret, nil
  1089. }
  1090. func fieldOperatorThreshold(opt monitor.CommonAlertFieldOpt, threshold float64) float64 {
  1091. if opt == monitor.CommonAlertFieldOptDivision && threshold > 1 {
  1092. return threshold / float64(100)
  1093. }
  1094. return threshold
  1095. }
  1096. func (alert *SCommonAlert) ValidateUpdateData(
  1097. ctx context.Context,
  1098. userCred mcclient.TokenCredential,
  1099. query jsonutils.JSONObject,
  1100. data *jsonutils.JSONDict,
  1101. ) (*jsonutils.JSONDict, error) {
  1102. generateName, _ := data.GetString("generate_name")
  1103. if len(generateName) != 0 && alert.Name != generateName {
  1104. name, err := db.GenerateName(ctx, CommonAlertManager, userCred, generateName)
  1105. if err != nil {
  1106. return data, err
  1107. }
  1108. data.Set("name", jsonutils.NewString(name))
  1109. }
  1110. statusUpdate := apis.StatusStandaloneResourceBaseUpdateInput{}
  1111. data.Unmarshal(&statusUpdate)
  1112. _, err := alert.SAlert.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, statusUpdate)
  1113. if err != nil {
  1114. return data, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
  1115. }
  1116. updataInput := new(monitor.CommonAlertUpdateInput)
  1117. if period, _ := data.GetString("period"); len(period) > 0 {
  1118. if _, err := time.ParseDuration(period); err != nil {
  1119. return data, httperrors.NewInputParameterError("Invalid period format: %s", period)
  1120. }
  1121. if period != "" {
  1122. frep, _ := time.ParseDuration(period)
  1123. freqSpec := int64(frep / time.Second)
  1124. dur, _ := data.Int("alert_duration")
  1125. if dur > 1 {
  1126. alertFor := freqSpec * dur
  1127. data.Set("for", jsonutils.NewInt(alertFor))
  1128. }
  1129. data.Set("frequency", jsonutils.NewInt(freqSpec))
  1130. }
  1131. }
  1132. if silentPeriod, _ := data.GetString("silent_period"); len(silentPeriod) > 0 {
  1133. if _, err := time.ParseDuration(silentPeriod); err != nil {
  1134. return data, httperrors.NewInputParameterError("Invalid silent_period format: %s", silentPeriod)
  1135. }
  1136. }
  1137. tmp := jsonutils.NewArray()
  1138. if metric_query, _ := data.GetArray("metric_query"); len(metric_query) > 0 {
  1139. for i := range metric_query {
  1140. query := new(monitor.CommonAlertQuery)
  1141. err := metric_query[i].Unmarshal(query)
  1142. if err != nil {
  1143. return data, errors.Wrap(err, "metric_query Unmarshal error")
  1144. }
  1145. if err := validateCommonAlertQuery(query); err != nil {
  1146. return data, err
  1147. }
  1148. if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok {
  1149. return data, httperrors.NewInputParameterError("the reduce is illegal: %s", query.Reduce)
  1150. }
  1151. /*if query.Threshold == 0 {
  1152. return data, httperrors.NewInputParameterError("threshold is meaningless")
  1153. }*/
  1154. if strings.Contains(query.From, "now-") {
  1155. query.To = "now"
  1156. query.From = "1h"
  1157. }
  1158. tmp.Add(jsonutils.Marshal(query))
  1159. }
  1160. data.Add(tmp, "metric_query")
  1161. metricQuery := new(monitor.CommonMetricInputQuery)
  1162. err := data.Unmarshal(metricQuery)
  1163. if err != nil {
  1164. return data, errors.Wrap(err, "metric_query Unmarshal error")
  1165. }
  1166. scope, _ := data.GetString("scope")
  1167. ownerId := CommonAlertManager.GetOwnerId(ctx, userCred, data)
  1168. err = CommonAlertManager.ValidateMetricQuery(metricQuery, scope, ownerId, true)
  1169. if err != nil {
  1170. return data, errors.Wrap(err, "metric query error")
  1171. }
  1172. if alert.getAlertType() == monitor.CommonAlertSystemAlertType {
  1173. forceUpdate, _ := data.Bool("force_update")
  1174. if !forceUpdate {
  1175. return data, nil
  1176. }
  1177. }
  1178. data.Update(jsonutils.Marshal(metricQuery))
  1179. err = data.Unmarshal(updataInput)
  1180. if err != nil {
  1181. return data, errors.Wrap(err, "updataInput Unmarshal err")
  1182. }
  1183. alertCreateInput, err := alert.getUpdateAlertInput(*updataInput)
  1184. if err != nil {
  1185. return data, errors.Wrap(err, "getUpdateAlertInput")
  1186. }
  1187. alertCreateInput, err = AlertManager.ValidateCreateData(ctx, userCred, nil, query, alertCreateInput)
  1188. if err != nil {
  1189. return data, err
  1190. }
  1191. data.Set("settings", jsonutils.Marshal(&alertCreateInput.Settings))
  1192. updataInput.AlertUpdateInput, err = alert.SAlert.ValidateUpdateData(ctx, userCred, query, updataInput.AlertUpdateInput)
  1193. if err != nil {
  1194. return data, errors.Wrap(err, "SAlert.ValidateUpdateData")
  1195. }
  1196. updataInput.For = alertCreateInput.For
  1197. data.Update(jsonutils.Marshal(updataInput))
  1198. }
  1199. return data, nil
  1200. }
  1201. func (manager *SCommonAlertManager) GetOwnerId(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) mcclient.IIdentityProvider {
  1202. ownId, _ := CommonAlertManager.FetchOwnerId(ctx, data)
  1203. if ownId == nil {
  1204. ownId = userCred
  1205. }
  1206. return ownId
  1207. }
  1208. func (alert *SCommonAlert) PostUpdate(
  1209. ctx context.Context, userCred mcclient.TokenCredential,
  1210. query jsonutils.JSONObject, data jsonutils.JSONObject) {
  1211. updateInput := new(monitor.CommonAlertUpdateInput)
  1212. data.Unmarshal(updateInput)
  1213. if updateInput.DisableNotifyRecovery != nil {
  1214. if _, err := db.Update(alert, func() error {
  1215. alert.DisableNotifyRecovery = *updateInput.DisableNotifyRecovery
  1216. return nil
  1217. }); err != nil {
  1218. log.Errorf("update disable_notify_recovery error: %v", err)
  1219. }
  1220. }
  1221. if len(updateInput.Channel) != 0 {
  1222. if err := alert.UpdateNotification(ctx, userCred, query, data); err != nil {
  1223. log.Errorf("update notification error: %v", err)
  1224. }
  1225. alert.SetChannel(ctx, updateInput.Channel)
  1226. } else if len(updateInput.SilentPeriod) != 0 {
  1227. if err := alert.updateNotificationSilentPeriod(updateInput.SilentPeriod); err != nil {
  1228. log.Errorf("update notification silent_period error: %v", err)
  1229. }
  1230. }
  1231. if _, err := data.GetString("scope"); err == nil {
  1232. _, err = alert.PerformSetScope(ctx, userCred, query, data)
  1233. if err != nil {
  1234. log.Errorf("Alert PerformSetScope: %v", err)
  1235. }
  1236. }
  1237. if updateInput.GetPointStr {
  1238. alert.setPointStr(ctx, userCred, strconv.FormatBool(updateInput.GetPointStr))
  1239. }
  1240. if len(updateInput.MetaName) != 0 {
  1241. alert.setMetaName(ctx, userCred, updateInput.MetaName)
  1242. }
  1243. CommonAlertManager.SetSubscriptionAlert(alert)
  1244. //alert.StartUpdateMonitorAlertJointTask(ctx, userCred)
  1245. }
  1246. func (alert *SCommonAlert) UpdateNotification(ctx context.Context, userCred mcclient.TokenCredential,
  1247. query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  1248. err := alert.customizeDeleteNotis(ctx, userCred, query, data)
  1249. if err != nil {
  1250. return errors.Wrap(err, "update notification err")
  1251. }
  1252. name, _ := data.GetString("name")
  1253. if len(name) == 0 {
  1254. data.(*jsonutils.JSONDict).Add(jsonutils.NewString(alert.Name), "name")
  1255. }
  1256. err = alert.customizeCreateNotis(ctx, userCred, query, data)
  1257. if err != nil {
  1258. log.Errorf("customizeCreateNotis for %s(%s): %v", alert.GetName(), alert.GetId(), err)
  1259. }
  1260. return err
  1261. }
  1262. func (alert *SCommonAlert) getUpdateAlertInput(updateInput monitor.CommonAlertUpdateInput) (monitor.AlertCreateInput, error) {
  1263. input := monitor.CommonAlertCreateInput{
  1264. CommonMetricInputQuery: updateInput.CommonMetricInputQuery,
  1265. Period: updateInput.Period,
  1266. }
  1267. input.AlertDuration = updateInput.AlertDuration
  1268. return CommonAlertManager.toAlertCreatInput(input)
  1269. }
  1270. func (alert *SCommonAlert) CustomizeDelete(
  1271. ctx context.Context, userCred mcclient.TokenCredential,
  1272. query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  1273. alert.SetStatus(ctx, userCred, monitor.ALERT_STATUS_DELETING, "")
  1274. err := alert.customizeDeleteNotis(ctx, userCred, query, data)
  1275. if err != nil {
  1276. alert.SetStatus(ctx, userCred, monitor.ALERT_STATUS_DELETE_FAIL, "")
  1277. return errors.Wrap(err, "customizeDeleteNotis")
  1278. }
  1279. alert.StartDeleteTask(ctx, userCred)
  1280. alert.StartDetachMonitorAlertJointTask(ctx, userCred)
  1281. return alert.SAlert.CustomizeDelete(ctx, userCred, query, data)
  1282. }
  1283. func (alert *SCommonAlert) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  1284. return alert.SStandaloneResourceBase.Delete(ctx, userCred)
  1285. }
  1286. func (self *SCommonAlert) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential) {
  1287. RunModelTask("DeleteAlertRecordTask", self, func() error {
  1288. onErr := func(err error) {
  1289. msg := jsonutils.NewString(err.Error())
  1290. // db.OpsLog.LogEvent(self, db.ACT_DELETE_FAIL, msg, userCred)
  1291. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_DELETE, msg, userCred, false)
  1292. }
  1293. errs := self.DeleteAttachAlertRecords(ctx, userCred)
  1294. if len(errs) != 0 {
  1295. err := errors.Wrapf(errors.NewAggregate(errs), "DeleteAttachAlertRecords of %s", self.GetName())
  1296. onErr(err)
  1297. return err
  1298. }
  1299. if err := self.RealDelete(ctx, userCred); err != nil {
  1300. err = errors.Wrapf(err, "RealDelete CommonAlert %s", self.GetName())
  1301. onErr(err)
  1302. return err
  1303. }
  1304. // db.OpsLog.LogEvent(self, db.ACT_DELETE, nil, userCred)
  1305. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_DELETE, nil, userCred, true)
  1306. return nil
  1307. })
  1308. }
  1309. func (self *SCommonAlert) DeleteAttachAlertRecords(ctx context.Context, userCred mcclient.TokenCredential) (errs []error) {
  1310. records, err := AlertRecordManager.GetAlertRecordsByAlertId(self.GetId())
  1311. if err != nil {
  1312. errs = append(errs, errors.Wrap(err, "GetAlertRecordsByAlertId error"))
  1313. return
  1314. }
  1315. for i, _ := range records {
  1316. err := records[i].Delete(ctx, userCred)
  1317. if err != nil {
  1318. errs = append(errs, errors.Wrapf(err, "delete attach record:%s error", records[i].GetId()))
  1319. }
  1320. }
  1321. return
  1322. }
  1323. func (alert *SCommonAlert) customizeDeleteNotis(
  1324. ctx context.Context, userCred mcclient.TokenCredential,
  1325. query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  1326. return alert.deleteNotifications(ctx, userCred, query, data)
  1327. }
  1328. func (alert *SCommonAlert) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  1329. CommonAlertManager.DeleteSubscriptionAlert(alert)
  1330. return nil
  1331. }
  1332. func (self *SCommonAlertManager) GetSystemAlerts() ([]SCommonAlert, error) {
  1333. objs := make([]SCommonAlert, 0)
  1334. q := CommonAlertManager.Query()
  1335. metaData := db.Metadata.Query().SubQuery()
  1336. q.Join(metaData, sqlchemy.Equals(
  1337. metaData.Field("obj_id"), q.Field("id")))
  1338. q.Filter(sqlchemy.AND(sqlchemy.Equals(metaData.Field("key"), CommonAlertMetadataAlertType),
  1339. sqlchemy.Equals(metaData.Field("value"), monitor.CommonAlertSystemAlertType)))
  1340. err := db.FetchModelObjects(self, q, &objs)
  1341. if err != nil {
  1342. return nil, err
  1343. }
  1344. return objs, nil
  1345. }
  1346. func (alert *SCommonAlert) PerformSetScope(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1347. domainId := jsonutils.GetAnyString(data, []string{"domain_id", "domain", "project_domain_id", "project_domain"})
  1348. projectId := jsonutils.GetAnyString(data, []string{"project_id", "project"})
  1349. if len(domainId) == 0 && len(projectId) == 0 {
  1350. scope, _ := data.GetString("scope")
  1351. if len(scope) != 0 {
  1352. switch rbacscope.TRbacScope(scope) {
  1353. case rbacscope.ScopeSystem:
  1354. case rbacscope.ScopeDomain:
  1355. domainId = userCred.GetProjectDomainId()
  1356. data.(*jsonutils.JSONDict).Set("domain_id", jsonutils.NewString(domainId))
  1357. case rbacscope.ScopeProject:
  1358. projectId = userCred.GetProjectId()
  1359. data.(*jsonutils.JSONDict).Set("project_id", jsonutils.NewString(projectId))
  1360. }
  1361. }
  1362. }
  1363. return db.PerformSetScope(ctx, alert, userCred, data)
  1364. }
  1365. func (manager *SCommonAlertManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  1366. var err error
  1367. q, err = manager.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  1368. if err == nil {
  1369. return q, nil
  1370. }
  1371. q, err = manager.SScopedResourceBaseManager.QueryDistinctExtraField(q, field)
  1372. if err == nil {
  1373. return q, nil
  1374. }
  1375. switch field {
  1376. case "status":
  1377. q.AppendField(sqlchemy.DISTINCT(field, q.Field("status"))).Distinct()
  1378. return q, nil
  1379. case "res_type":
  1380. resTypeQuery := MetricMeasurementManager.Query("res_type").Distinct()
  1381. return resTypeQuery, nil
  1382. }
  1383. return q, httperrors.ErrNotFound
  1384. }
  1385. func (alert *SCommonAlert) PerformConfig(ctx context.Context, userCred mcclient.TokenCredential,
  1386. query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1387. period, _ := data.GetString("period")
  1388. comparator, _ := data.GetString("comparator")
  1389. threshold, _ := data.GetString("threshold")
  1390. thresholdRange, _ := data.GetArray("threshold_range")
  1391. if len(period) != 0 {
  1392. if _, err := time.ParseDuration(period); err != nil {
  1393. return data, httperrors.NewInputParameterError("Invalid period format: %s", period)
  1394. }
  1395. }
  1396. reason, _ := data.GetString("reason")
  1397. if err := validateComparatorAndThreshold(comparator, threshold, thresholdRange); err != nil {
  1398. return data, err
  1399. }
  1400. _, err := db.Update(alert, func() error {
  1401. if len(period) != 0 {
  1402. freq, _ := time.ParseDuration(period)
  1403. alert.Frequency = int64(freq / time.Second)
  1404. }
  1405. setting, _ := alert.GetSettings()
  1406. if len(comparator) != 0 {
  1407. evalType := getQueryEvalType(comparator)
  1408. setting.Conditions[0].Evaluator.Type = string(evalType)
  1409. }
  1410. // 处理 ranged types
  1411. if len(thresholdRange) >= 2 {
  1412. vals := make([]float64, 2)
  1413. for i := 0; i < 2 && i < len(thresholdRange); i++ {
  1414. val, _ := strconv.ParseFloat(thresholdRange[i].String(), 64)
  1415. vals[i] = fieldOperatorThreshold("", val)
  1416. }
  1417. setting.Conditions[0].Evaluator.Params = vals
  1418. } else if len(threshold) != 0 {
  1419. val, _ := strconv.ParseFloat(threshold, 64)
  1420. setting.Conditions[0].Evaluator.Params = []float64{fieldOperatorThreshold("", val)}
  1421. }
  1422. alert.Settings = setting
  1423. if len(reason) != 0 {
  1424. alert.Reason = reason
  1425. }
  1426. disableNotifyRecovery, gErr := data.Bool("disable_notify_recovery")
  1427. if gErr == nil {
  1428. alert.DisableNotifyRecovery = disableNotifyRecovery
  1429. }
  1430. return nil
  1431. })
  1432. PerformConfigLog(alert, userCred)
  1433. return jsonutils.Marshal(alert), err
  1434. }
  1435. func PerformConfigLog(model db.IModel, userCred mcclient.TokenCredential) {
  1436. // db.OpsLog.LogEvent(model, db.ACT_UPDATE_RULE, "", userCred)
  1437. logclient.AddSimpleActionLog(model, logclient.ACT_UPDATE_RULE, nil, userCred, true)
  1438. }
  1439. func (alert *SCommonAlert) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
  1440. err := db.EnabledPerformEnable(alert, ctx, userCred, true)
  1441. if err != nil {
  1442. return nil, errors.Wrap(err, "EnabledPerformEnable")
  1443. }
  1444. //alert.StartUpdateMonitorAlertJointTask(ctx, userCred)
  1445. return nil, nil
  1446. }
  1447. func (alert *SCommonAlert) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
  1448. err := db.EnabledPerformEnable(alert, ctx, userCred, false)
  1449. if err != nil {
  1450. return nil, errors.Wrap(err, "EnabledPerformEnable")
  1451. }
  1452. err = alert.StartDetachTask(ctx, userCred)
  1453. if err != nil {
  1454. return nil, errors.Wrap(err, "alert StartDetachTask error")
  1455. }
  1456. return nil, nil
  1457. }
  1458. func (alert *SCommonAlert) StartDetachTask(ctx context.Context, userCred mcclient.TokenCredential) error {
  1459. RunModelTask("DetachAlertResourceTask", alert, func() error {
  1460. onErr := func(err error) {
  1461. msg := jsonutils.NewString(err.Error())
  1462. // db.OpsLog.LogEvent(alert, db.ACT_DETACH, msg, userCred)
  1463. logclient.AddActionLogWithContext(ctx, alert, logclient.ACT_DETACH_ALERTRESOURCE, msg, userCred, false)
  1464. }
  1465. errs := alert.DetachAlertResourceOnDisable(ctx, userCred)
  1466. if len(errs) != 0 {
  1467. err := errors.Wrapf(errors.NewAggregate(errs), "DetachAlertResourceOnDisable of alert %s", alert.GetName())
  1468. onErr(err)
  1469. return err
  1470. }
  1471. if err := MonitorResourceAlertManager.DetachJoint(ctx, userCred,
  1472. monitor.MonitorResourceJointListInput{AlertId: alert.GetId()}); err != nil {
  1473. log.Errorf("DetachJoint when alert(%s) disable: %v", alert.GetName(), err)
  1474. }
  1475. logclient.AddActionLogWithContext(ctx, alert, logclient.ACT_DETACH_ALERTRESOURCE, nil, userCred, true)
  1476. return nil
  1477. })
  1478. return nil
  1479. }
  1480. func (alert *SCommonAlert) DetachAlertResourceOnDisable(ctx context.Context,
  1481. userCred mcclient.TokenCredential) (errs []error) {
  1482. return CommonAlertManager.DetachAlertResourceByAlertId(ctx, userCred, alert.Id)
  1483. }
  1484. func (manager *SCommonAlertManager) DetachAlertResourceByAlertId(ctx context.Context,
  1485. userCred mcclient.TokenCredential, alertId string) (errs []error) {
  1486. resources, err := GetAlertResourceManager().getResourceFromAlertId(alertId)
  1487. if err != nil {
  1488. errs = append(errs, errors.Wrap(err, "getResourceFromAlert error"))
  1489. return
  1490. }
  1491. for _, resource := range resources {
  1492. err := resource.DetachAlert(ctx, userCred, alertId)
  1493. if err != nil {
  1494. errs = append(errs, errors.Wrapf(err, "resource:%s DetachAlert:%s err", resource.Id, alertId))
  1495. }
  1496. }
  1497. return
  1498. }
  1499. func (alert *SCommonAlert) StartUpdateMonitorAlertJointTask(ctx context.Context, userCred mcclient.TokenCredential) {
  1500. RunModelTask("UpdateMonitorResourceJointTask", alert, func() error {
  1501. if err := alert.UpdateMonitorResourceJoint(ctx, userCred); err != nil {
  1502. return errors.Wrapf(err, "UpdateMonitorResourceJoint of alert %s", alert.GetName())
  1503. }
  1504. return nil
  1505. })
  1506. }
  1507. func (alert *SCommonAlert) UpdateMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
  1508. var resType string
  1509. setting, _ := alert.GetSettings()
  1510. for _, con := range setting.Conditions {
  1511. measurement, _ := MetricMeasurementManager.GetCache().Get(con.Query.Model.Measurement)
  1512. if measurement == nil {
  1513. resType = monitor.METRIC_RES_TYPE_HOST
  1514. } else {
  1515. resType = measurement.ResType
  1516. }
  1517. }
  1518. ret, err := alert.TestRunAlert(userCred, monitor.AlertTestRunInput{})
  1519. if err != nil {
  1520. return errors.Wrapf(err, "TestRunAlert %s", alert.GetName())
  1521. }
  1522. if len(ret.AlertOKEvalMatches) > 0 {
  1523. matches := make([]monitor.EvalMatch, len(ret.AlertOKEvalMatches))
  1524. for i := range ret.AlertOKEvalMatches {
  1525. matches[i] = *ret.AlertOKEvalMatches[i]
  1526. }
  1527. input := &UpdateMonitorResourceAlertInput{
  1528. AlertId: alert.GetId(),
  1529. Matches: matches,
  1530. ResType: resType,
  1531. AlertState: string(monitor.AlertStateOK),
  1532. SendState: monitor.SEND_STATE_SILENT,
  1533. TriggerTime: time.Now(),
  1534. AlertRecordId: "",
  1535. }
  1536. if err := MonitorResourceManager.UpdateMonitorResourceAttachJoint(ctx, userCred, input); err != nil {
  1537. return errors.Wrap(err, "UpdateMonitorResourceAttachJoint")
  1538. }
  1539. return nil
  1540. }
  1541. return nil
  1542. }
  1543. func (alert *SCommonAlert) StartDetachMonitorAlertJointTask(ctx context.Context, userCred mcclient.TokenCredential) {
  1544. RunModelTask("DetachMonitorResourceJointTask", alert, func() error {
  1545. if err := alert.DetachMonitorResourceJoint(ctx, userCred); err != nil {
  1546. err = errors.Wrapf(err, "DetachMonitorResourceJoint of alert %s", alert.GetName())
  1547. msg := jsonutils.NewString(err.Error())
  1548. // db.OpsLog.LogEvent(alert, db.ACT_DETACH_MONITOR_RESOURCE_JOINT, msg, userCred)
  1549. logclient.AddActionLogWithContext(ctx, alert, logclient.ACT_DETACH_MONITOR_RESOURCE_JOINT, msg, userCred, false)
  1550. return err
  1551. }
  1552. logclient.AddActionLogWithContext(ctx, alert, logclient.ACT_DETACH_MONITOR_RESOURCE_JOINT, nil, userCred, true)
  1553. return nil
  1554. })
  1555. }
  1556. func (alert *SCommonAlert) DetachMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
  1557. err := MonitorResourceAlertManager.DetachJoint(ctx, userCred, monitor.MonitorResourceJointListInput{AlertId: alert.GetId()})
  1558. if err != nil {
  1559. return errors.Wrap(err, "SCommonAlert DetachJoint err")
  1560. }
  1561. return nil
  1562. }
  1563. func (alert *SCommonAlert) UpdateResType() error {
  1564. setting, _ := alert.GetSettings()
  1565. if len(setting.Conditions) == 0 {
  1566. return nil
  1567. }
  1568. measurement, _ := MetricMeasurementManager.GetCache().Get(setting.Conditions[0].Query.Model.Measurement)
  1569. if measurement == nil {
  1570. return nil
  1571. }
  1572. _, err := db.Update(alert, func() error {
  1573. alert.ResType = measurement.ResType
  1574. return nil
  1575. })
  1576. if err != nil {
  1577. return errors.Wrapf(err, "alert:%s UpdateResType err", alert.Name)
  1578. }
  1579. return nil
  1580. }
  1581. func (alert *SCommonAlert) GetSilentPeriod() (int64, error) {
  1582. notis, err := alert.GetNotifications()
  1583. if err != nil {
  1584. return 0, errors.Wrap(err, "GetNotifications")
  1585. }
  1586. for _, n := range notis {
  1587. noti, _ := n.GetNotification()
  1588. if noti != nil && noti.Frequency != 0 {
  1589. return noti.Frequency, nil
  1590. }
  1591. }
  1592. return 0, nil
  1593. }
  1594. func (alert *SCommonAlert) updateNotificationSilentPeriod(silentPeriod string) error {
  1595. duration, _ := time.ParseDuration(silentPeriod)
  1596. frequency := int64(duration / time.Second)
  1597. notis, err := alert.GetNotifications()
  1598. if err != nil {
  1599. return errors.Wrap(err, "GetNotifications")
  1600. }
  1601. for _, n := range notis {
  1602. noti, _ := n.GetNotification()
  1603. if noti != nil {
  1604. if _, err := db.Update(noti, func() error {
  1605. noti.Frequency = frequency
  1606. return nil
  1607. }); err != nil {
  1608. return errors.Wrapf(err, "update notification %s frequency", noti.GetId())
  1609. }
  1610. }
  1611. }
  1612. return nil
  1613. }
  1614. func (alert *SCommonAlert) GetAlertRules(silentPeriod int64) ([]*monitor.AlertRecordRule, error) {
  1615. rules := make([]*monitor.AlertRecordRule, 0)
  1616. settings, err := alert.GetSettings()
  1617. if err != nil {
  1618. return nil, errors.Wrapf(err, "get alert %s settings", alert.GetId())
  1619. }
  1620. for index := range settings.Conditions {
  1621. rule := alert.GetAlertRule(settings, index, silentPeriod)
  1622. rules = append(rules, rule)
  1623. }
  1624. return rules, nil
  1625. }
  1626. func (alert *SCommonAlert) GetAlertRule(settings *monitor.AlertSetting, index int, silentPeriod int64) *monitor.AlertRecordRule {
  1627. alertDetails := alert.GetCommonAlertMetricDetailsFromAlertCondition(index, &settings.Conditions[index])
  1628. rule := &monitor.AlertRecordRule{
  1629. ResType: alertDetails.ResType,
  1630. Metric: fmt.Sprintf("%s.%s", alertDetails.Measurement, alertDetails.Field),
  1631. Measurement: alertDetails.Measurement,
  1632. Database: alertDetails.DB,
  1633. MeasurementDesc: alertDetails.MeasurementDisplayName,
  1634. Field: alertDetails.Field,
  1635. FieldDesc: alertDetails.FieldDescription.DisplayName,
  1636. Comparator: alertDetails.Comparator,
  1637. Unit: alertDetails.FieldDescription.Unit,
  1638. Threshold: RationalizeValueFromUnit(alertDetails.Threshold, alertDetails.FieldDescription.Unit, ""),
  1639. ThresholdRange: alertDetails.ThresholdRange,
  1640. ConditionType: alertDetails.ConditionType,
  1641. Reducer: alertDetails.Reduce,
  1642. }
  1643. if len(rule.ResType) == 0 {
  1644. if alertDetails.DB == monitor.METRIC_DATABASE_TELE {
  1645. rule.ResType = monitor.METRIC_RES_TYPE_HOST
  1646. }
  1647. }
  1648. if alert.Frequency < 60 {
  1649. rule.Period = fmt.Sprintf("%ds", alert.Frequency)
  1650. } else {
  1651. rule.Period = fmt.Sprintf("%dm", alert.Frequency/60)
  1652. }
  1653. rule.AlertDuration = alert.For / alert.Frequency
  1654. if rule.AlertDuration == 0 {
  1655. rule.AlertDuration = 1
  1656. }
  1657. if silentPeriod > 0 {
  1658. rule.SilentPeriod = fmt.Sprintf("%dm", silentPeriod/60)
  1659. }
  1660. return rule
  1661. }
  1662. func (alert *SCommonAlert) GetResourceAlert(resourceId string, metric string) (*SMonitorResourceAlert, error) {
  1663. return MonitorResourceAlertManager.GetResourceAlert(alert.GetId(), resourceId, metric)
  1664. }
  1665. func (alert *SCommonAlert) IsResourceMetricAlerting(resourceId string, metric string) (bool, error) {
  1666. ra, err := alert.GetResourceAlert(resourceId, metric)
  1667. if err != nil {
  1668. return false, errors.Wrapf(err, "GetResourceAlert")
  1669. }
  1670. if ra.AlertState == string(monitor.AlertStateAlerting) {
  1671. return true, nil
  1672. }
  1673. return false, nil
  1674. }
  1675. var fileSize = []string{"bps", "Bps", "byte"}
  1676. func RationalizeValueFromUnit(value float64, unit string, opt string) string {
  1677. if utils.IsInStringArray(unit, fileSize) {
  1678. if unit == "byte" {
  1679. return (FormatFileSize(value, unit, float64(1024)))
  1680. }
  1681. return FormatFileSize(value, unit, float64(1000))
  1682. }
  1683. if unit == "%" && monitor.CommonAlertFieldOptDivision == monitor.CommonAlertFieldOpt(opt) {
  1684. return fmt.Sprintf("%0.2f%s", value*100, unit)
  1685. }
  1686. return fmt.Sprintf("%0.2f%s", value, unit)
  1687. }
  1688. // 单位转换 保留2位小数
  1689. func FormatFileSize(fileSize float64, unit string, unitsize float64) (size string) {
  1690. if fileSize < unitsize {
  1691. return fmt.Sprintf("%.2f%s", fileSize, unit)
  1692. } else if fileSize < (unitsize * unitsize) {
  1693. return fmt.Sprintf("%.2fK%s", float64(fileSize)/float64(unitsize), unit)
  1694. } else if fileSize < (unitsize * unitsize * unitsize) {
  1695. return fmt.Sprintf("%.2fM%s", float64(fileSize)/float64(unitsize*unitsize), unit)
  1696. } else if fileSize < (unitsize * unitsize * unitsize * unitsize) {
  1697. return fmt.Sprintf("%.2fG%s", float64(fileSize)/float64(unitsize*unitsize*unitsize), unit)
  1698. } else if fileSize < (unitsize * unitsize * unitsize * unitsize * unitsize) {
  1699. return fmt.Sprintf("%.2fT%s", float64(fileSize)/float64(unitsize*unitsize*unitsize*unitsize), unit)
  1700. } else { //if fileSize < (1024 * 1024 * 1024 * 1024 * 1024 * 1024)
  1701. return fmt.Sprintf("%.2fE%s", float64(fileSize)/float64(unitsize*unitsize*unitsize*unitsize*unitsize), unit)
  1702. }
  1703. }
  1704. type SCompanyInfo struct {
  1705. Copyright string `json:"copyright"`
  1706. Name string `json:"name"`
  1707. }
  1708. func GetCompanyInfo(ctx context.Context) (SCompanyInfo, error) {
  1709. /* info, err := getBrandFromCopyrightApi(ctx)
  1710. if err == nil && len(info.Name) != 0 {
  1711. return *info, nil
  1712. }
  1713. if err != nil {
  1714. log.Errorf("getBrandFromCopyrightApi err:%v", err)
  1715. }
  1716. return getBrandFromInfoApi(ctx)
  1717. */
  1718. return SCompanyInfo{
  1719. Name: options.Options.GetPlatformName(appctx.Lang(ctx)),
  1720. }, nil
  1721. }
  1722. /*
  1723. func getBrandFromCopyrightApi(ctx context.Context) (*SCompanyInfo, error) {
  1724. session := auth.GetAdminSession(context.Background(), "", "")
  1725. obj, err := modules.Copyright.Update(session, "copyright", jsonutils.NewDict())
  1726. if err != nil {
  1727. return nil, err
  1728. }
  1729. var info SCompanyInfo
  1730. lang := i18n.Lang(ctx)
  1731. switch lang {
  1732. case language.English:
  1733. info.Name, _ = obj.GetString("brand_en")
  1734. default:
  1735. info.Name, _ = obj.GetString("brand_cn")
  1736. }
  1737. return &info, nil
  1738. }
  1739. func getBrandFromInfoApi(ctx context.Context) (SCompanyInfo, error) {
  1740. session := auth.GetAdminSession(context.Background(), "", "")
  1741. obj, err := modules.Info.Get(session, "info", jsonutils.NewDict())
  1742. if err != nil {
  1743. return SCompanyInfo{}, err
  1744. }
  1745. var info SCompanyInfo
  1746. err = obj.Unmarshal(&info)
  1747. if err != nil {
  1748. return SCompanyInfo{}, err
  1749. }
  1750. if strings.Contains(info.Copyright, COMPANY_COPYRIGHT_ONECLOUD) {
  1751. lang := i18n.Lang(ctx)
  1752. switch lang {
  1753. case language.English:
  1754. info.Name = BRAND_ONECLOUD_NAME_EN
  1755. default:
  1756. info.Name = BRAND_ONECLOUD_NAME_CN
  1757. }
  1758. }
  1759. return info, nil
  1760. }
  1761. */