monitor_resource.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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. "reflect"
  18. "strings"
  19. "sync"
  20. "time"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/rbacscope"
  25. "yunion.io/x/pkg/utils"
  26. "yunion.io/x/sqlchemy"
  27. "yunion.io/x/onecloud/pkg/apihelper"
  28. "yunion.io/x/onecloud/pkg/apis/monitor"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  30. "yunion.io/x/onecloud/pkg/httperrors"
  31. "yunion.io/x/onecloud/pkg/mcclient"
  32. "yunion.io/x/onecloud/pkg/mcclient/auth"
  33. "yunion.io/x/onecloud/pkg/util/stringutils2"
  34. )
  35. var (
  36. MonitorResourceManager *SMonitorResourceManager
  37. )
  38. // validateTopQueryInput 验证 TopQueryInput 参数并返回解析后的值
  39. func validateTopQueryInput(input monitor.TopQueryInput) (startTime time.Time, endTime time.Time, top int, err error) {
  40. startTime = input.StartTime
  41. endTime = input.EndTime
  42. if startTime.IsZero() || endTime.IsZero() {
  43. return time.Time{}, time.Time{}, 0, httperrors.NewInputParameterError("start_time and end_time must be specified")
  44. }
  45. if startTime.After(endTime) {
  46. return time.Time{}, time.Time{}, 0, httperrors.NewInputParameterError("start_time must be before end_time")
  47. }
  48. top = *input.Top
  49. if top <= 0 {
  50. top = 5 // 默认返回 top 5
  51. }
  52. return startTime, endTime, top, nil
  53. }
  54. type IMonitorResourceCache interface {
  55. Get(resId string) (jsonutils.JSONObject, bool)
  56. }
  57. type sMonitorResourceCache struct {
  58. length int
  59. sync.Map
  60. }
  61. func (c *sMonitorResourceCache) set(resId string, obj jsonutils.JSONObject) {
  62. c.Store(resId, obj)
  63. c.length++
  64. }
  65. func (c *sMonitorResourceCache) remove(resId string) {
  66. c.Delete(resId)
  67. c.length--
  68. }
  69. func (c *sMonitorResourceCache) Get(resId string) (jsonutils.JSONObject, bool) {
  70. obj, ok := c.Load(resId)
  71. if !ok {
  72. return nil, false
  73. }
  74. return obj.(jsonutils.JSONObject), true
  75. }
  76. func init() {
  77. MonitorResourceManager = &SMonitorResourceManager{
  78. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  79. &SMonitorResource{},
  80. "monitor_resource_tbl",
  81. "monitorresource",
  82. "monitorresources",
  83. ),
  84. monitorResModelSets: NewModelSets(),
  85. }
  86. MonitorResourceManager.SetVirtualObject(MonitorResourceManager)
  87. RegistryResourceSync(NewGuestResourceSync())
  88. RegistryResourceSync(NewHostResourceSync())
  89. RegistryResourceSync(NewRdsResourceSync())
  90. RegistryResourceSync(NewRedisResourceSync())
  91. RegistryResourceSync(NewOssResourceSync())
  92. RegistryResourceSync(NewAccountResourceSync())
  93. RegistryResourceSync(NewStorageResourceSync())
  94. }
  95. func (manager *SMonitorResourceManager) GetModelSets() *MonitorResModelSets {
  96. return manager.monitorResModelSets
  97. }
  98. // +onecloud:swagger-gen-model-singular=monitorresource
  99. // +onecloud:swagger-gen-model-plural=monitorresources
  100. type SMonitorResourceManager struct {
  101. db.SVirtualResourceBaseManager
  102. db.SEnabledResourceBaseManager
  103. monitorResModelSets *MonitorResModelSets
  104. apih *apihelper.APIHelper
  105. }
  106. func (manager *SMonitorResourceManager) SetAPIHelper(h *apihelper.APIHelper) {
  107. if manager.apih != nil {
  108. panic("MonitorResourceManager's apihelper already set")
  109. }
  110. manager.apih = h
  111. }
  112. type SMonitorResource struct {
  113. db.SVirtualResourceBase
  114. db.SEnabledResourceBase
  115. AlertState string `width:"36" charset:"ascii" list:"user" default:"init" update:"user" json:"alert_state"`
  116. ResId string `width:"256" charset:"ascii" index:"true" list:"user" update:"user" json:"res_id"`
  117. ResType string `width:"36" charset:"ascii" list:"user" update:"user" json:"res_type"`
  118. }
  119. func (manager *SMonitorResourceManager) GetMonitorResources(input monitor.MonitorResourceListInput) ([]SMonitorResource, error) {
  120. monitorResources := make([]SMonitorResource, 0)
  121. query := manager.Query()
  122. if input.OnlyResId {
  123. query = query.AppendField(query.Field("id"), query.Field("res_id"))
  124. }
  125. query = manager.FieldListFilter(query, input)
  126. err := db.FetchModelObjects(manager, query, &monitorResources)
  127. if err != nil {
  128. return nil, errors.Wrap(err, "SMonitorResourceManager FetchModelObjects err")
  129. }
  130. return monitorResources, nil
  131. }
  132. func (man *SMonitorResourceManager) GetMonitorResourceByResId(id string) (*SMonitorResource, error) {
  133. resources, err := MonitorResourceManager.GetMonitorResources(monitor.MonitorResourceListInput{ResId: []string{id}})
  134. if err != nil {
  135. return nil, errors.Wrapf(err, "SMonitorResourceManager GetMonitorResources by resId: %s", id)
  136. }
  137. if len(resources) == 0 {
  138. return nil, errors.Errorf("SMonitorResourceManager GetMonitorResources by resId: %s not found", id)
  139. }
  140. return &resources[0], nil
  141. }
  142. type SdeleteRes struct {
  143. resType string
  144. notIn []string
  145. in []string
  146. }
  147. func (manager *SMonitorResourceManager) DeleteMonitorResources(ctx context.Context, userCred mcclient.TokenCredential, input SdeleteRes) error {
  148. monitorResources := make([]SMonitorResource, 0)
  149. errs := make([]error, 0)
  150. query := manager.Query()
  151. if len(input.notIn) != 0 {
  152. query.NotIn("res_id", input.notIn)
  153. }
  154. if len(input.in) != 0 {
  155. query.In("res_id", input.in)
  156. }
  157. if len(input.resType) != 0 {
  158. query.Equals("res_type", input.resType)
  159. }
  160. err := db.FetchModelObjects(manager, query, &monitorResources)
  161. if err != nil {
  162. return errors.Wrap(err, "SMonitorResourceManager FetchModelObjects when DeleteMonitorResources err")
  163. }
  164. for _, res := range monitorResources {
  165. err := (&res).RealDelete(ctx, userCred)
  166. if err != nil {
  167. errs = append(errs, errors.Wrapf(err, "delete monitorResource:%s err", res.GetId()))
  168. }
  169. }
  170. if len(errs) != 0 {
  171. return errors.NewAggregate(errs)
  172. }
  173. return nil
  174. }
  175. func (manager *SMonitorResourceManager) GetMonitorResourceById(id string) (*SMonitorResource, error) {
  176. iModel, err := db.FetchById(manager, id)
  177. if err != nil {
  178. return nil, errors.Wrapf(err, "GetMonitorResourceById:%s err", id)
  179. }
  180. return iModel.(*SMonitorResource), nil
  181. }
  182. func (manager *SMonitorResourceManager) ListItemFilter(
  183. ctx context.Context, q *sqlchemy.SQuery,
  184. userCred mcclient.TokenCredential,
  185. query monitor.MonitorResourceListInput,
  186. ) (*sqlchemy.SQuery, error) {
  187. // 如果指定了时间段和 top 参数,执行特殊的 top 查询
  188. if query.Top != nil {
  189. return manager.getTopResourcesByAlertCount(ctx, q, userCred, query)
  190. }
  191. var err error
  192. q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  193. if err != nil {
  194. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  195. }
  196. q, err = manager.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledResourceBaseListInput)
  197. if err != nil {
  198. return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter")
  199. }
  200. q = manager.FieldListFilter(q, query)
  201. return q, nil
  202. }
  203. func (manager *SMonitorResourceManager) FieldListFilter(q *sqlchemy.SQuery, query monitor.MonitorResourceListInput) *sqlchemy.SQuery {
  204. if len(query.ResType) != 0 {
  205. q.Equals("res_type", query.ResType)
  206. }
  207. if len(query.ResId) != 0 {
  208. q.In("res_id", query.ResId)
  209. }
  210. if len(query.ResName) != 0 {
  211. q.Contains("name", query.ResName)
  212. }
  213. if len(query.AlertStates) != 0 {
  214. q.In("alert_state", query.AlertStates)
  215. }
  216. return q
  217. }
  218. func (man *SMonitorResourceManager) OrderByExtraFields(
  219. ctx context.Context,
  220. q *sqlchemy.SQuery,
  221. userCred mcclient.TokenCredential,
  222. input monitor.MonitorResourceListInput,
  223. ) (*sqlchemy.SQuery, error) {
  224. var err error
  225. q, err = man.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.VirtualResourceListInput)
  226. if err != nil {
  227. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
  228. }
  229. return q, nil
  230. }
  231. // getTopResourcesByAlertCount 查询指定时间段内报警数量最多的 top N 资源
  232. func (man *SMonitorResourceManager) getTopResourcesByAlertCount(
  233. ctx context.Context,
  234. q *sqlchemy.SQuery,
  235. userCred mcclient.TokenCredential,
  236. query monitor.MonitorResourceListInput,
  237. ) (*sqlchemy.SQuery, error) {
  238. // 验证时间段和 top 参数
  239. startTime, endTime, top, err := validateTopQueryInput(query.TopQueryInput)
  240. if err != nil {
  241. return nil, err
  242. }
  243. // 查询指定时间段内的 AlertRecord
  244. recordQuery := AlertRecordManager.Query("res_ids", "res_type")
  245. recordQuery = recordQuery.GE("created_at", startTime).LE("created_at", endTime)
  246. recordQuery = recordQuery.IsNotNull("res_type").IsNotEmpty("res_type")
  247. recordQuery = recordQuery.IsNotEmpty("res_ids")
  248. // 如果指定了 ResType,添加过滤条件
  249. if len(query.ResType) > 0 {
  250. recordQuery = recordQuery.Equals("res_type", query.ResType)
  251. }
  252. // 应用权限过滤 - 使用 FilterByOwner 方法
  253. // 从 query 中获取 scope,如果没有则使用默认值
  254. scope := rbacscope.ScopeSystem
  255. if len(query.VirtualResourceListInput.Scope) > 0 {
  256. scope = rbacscope.TRbacScope(query.VirtualResourceListInput.Scope)
  257. }
  258. recordQuery = AlertRecordManager.SMonitorScopedResourceManager.FilterByOwner(
  259. ctx, recordQuery, AlertRecordManager, userCred, userCred, scope)
  260. // 执行查询获取所有记录
  261. type RecordRow struct {
  262. ResIds string
  263. ResType string
  264. }
  265. rows := make([]RecordRow, 0)
  266. err = recordQuery.All(&rows)
  267. if err != nil {
  268. return nil, errors.Wrap(err, "query alert records")
  269. }
  270. // 统计每个资源的报警数量
  271. resourceAlertCount := make(map[string]int)
  272. for _, row := range rows {
  273. if len(row.ResIds) == 0 {
  274. continue
  275. }
  276. // 解析 res_ids(逗号分隔)
  277. resIds := strings.Split(row.ResIds, ",")
  278. for _, resId := range resIds {
  279. resId = strings.TrimSpace(resId)
  280. if len(resId) > 0 {
  281. // 如果指定了 ResType,需要匹配 res_type
  282. if len(query.ResType) > 0 && row.ResType != query.ResType {
  283. continue
  284. }
  285. resourceAlertCount[resId]++
  286. }
  287. }
  288. }
  289. // 转换为切片并按报警数量排序
  290. type ResourceCount struct {
  291. ResId string
  292. Count int
  293. }
  294. resourceCounts := make([]ResourceCount, 0, len(resourceAlertCount))
  295. for resId, count := range resourceAlertCount {
  296. resourceCounts = append(resourceCounts, ResourceCount{
  297. ResId: resId,
  298. Count: count,
  299. })
  300. }
  301. // 按报警数量降序排序
  302. for i := 0; i < len(resourceCounts)-1; i++ {
  303. for j := i + 1; j < len(resourceCounts); j++ {
  304. if resourceCounts[i].Count < resourceCounts[j].Count {
  305. resourceCounts[i], resourceCounts[j] = resourceCounts[j], resourceCounts[i]
  306. }
  307. }
  308. }
  309. // 获取 top N 的资源 ID
  310. topResIds := make([]string, 0, top)
  311. for i := 0; i < top && i < len(resourceCounts); i++ {
  312. topResIds = append(topResIds, resourceCounts[i].ResId)
  313. }
  314. if len(topResIds) == 0 {
  315. // 如果没有找到任何记录,返回空查询
  316. return q.FilterByFalse(), nil
  317. }
  318. // 用 top res_id 过滤 MonitorResource 查询
  319. q, err = man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  320. if err != nil {
  321. return nil, err
  322. }
  323. q, err = man.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledResourceBaseListInput)
  324. if err != nil {
  325. return nil, err
  326. }
  327. q = man.FieldListFilter(q, query)
  328. q = q.In("res_id", topResIds)
  329. return q, nil
  330. }
  331. func (man *SMonitorResourceManager) HasName() bool {
  332. return false
  333. }
  334. func (man *SMonitorResourceManager) ValidateCreateData(
  335. ctx context.Context, userCred mcclient.TokenCredential,
  336. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
  337. data monitor.MonitorResourceCreateInput) (monitor.MonitorResourceCreateInput, error) {
  338. //rule 查询到资源信息后没有将资源id,进行转换
  339. if len(data.ResId) == 0 {
  340. return data, httperrors.NewInputParameterError("not found res_id %q", data.ResId)
  341. }
  342. if len(data.ResType) == 0 {
  343. return data, httperrors.NewInputParameterError("not found res_type %q", data.ResType)
  344. }
  345. return data, nil
  346. }
  347. func (self *SMonitorResource) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential,
  348. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  349. return nil
  350. }
  351. func (man *SMonitorResourceManager) FetchCustomizeColumns(
  352. ctx context.Context,
  353. userCred mcclient.TokenCredential,
  354. query jsonutils.JSONObject,
  355. objs []interface{},
  356. fields stringutils2.SSortedStrings,
  357. isList bool,
  358. ) []monitor.MonitorResourceDetails {
  359. rows := make([]monitor.MonitorResourceDetails, len(objs))
  360. virtRows := man.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  361. resIds := make([]string, len(objs))
  362. for i := range rows {
  363. rows[i] = monitor.MonitorResourceDetails{
  364. VirtualResourceDetails: virtRows[i],
  365. }
  366. mr := objs[i].(*SMonitorResource)
  367. resIds[i] = mr.ResId
  368. _, object := MonitorResourceManager.GetResourceObj(mr.ResId)
  369. if object != nil {
  370. object.Unmarshal(&rows[i])
  371. }
  372. }
  373. sq := MonitorResourceAlertManager.Query().In("monitor_resource_id", resIds).SubQuery()
  374. q := sq.Query(
  375. sq.Field("monitor_resource_id"),
  376. sqlchemy.COUNT("count", sq.Field("row_id")),
  377. ).GroupBy(sq.Field("monitor_resource_id"))
  378. mrs := []struct {
  379. MonitorResourceId string
  380. Count int64
  381. }{}
  382. err := q.All(&mrs)
  383. if err != nil {
  384. log.Errorf("query monitor resource alert error: %v", err)
  385. return rows
  386. }
  387. mrMap := make(map[string]int64)
  388. for _, mr := range mrs {
  389. mrMap[mr.MonitorResourceId] = mr.Count
  390. }
  391. for i := range rows {
  392. rows[i].AttachAlertCount = mrMap[resIds[i]]
  393. }
  394. return rows
  395. }
  396. func (self *SMonitorResource) AttachAlert(ctx context.Context, userCred mcclient.TokenCredential, alertId string, metric string, match monitor.EvalMatch) (*SMonitorResourceAlert, error) {
  397. iModel, _ := db.NewModelObject(MonitorResourceAlertManager)
  398. input := monitor.MonitorResourceJointCreateInput{
  399. MonitorResourceId: self.ResId,
  400. AlertId: alertId,
  401. AlertState: monitor.MONITOR_RESOURCE_ALERT_STATUS_ATTACH,
  402. Metric: metric,
  403. Data: match,
  404. }
  405. data := input.JSON(&input)
  406. err := data.Unmarshal(iModel)
  407. if err != nil {
  408. return nil, errors.Wrap(err, "MonitorResourceJointCreateInput unmarshal to joint err")
  409. }
  410. if err := MonitorResourceAlertManager.TableSpec().Insert(ctx, iModel); err != nil {
  411. return nil, errors.Wrap(err, "insert MonitorResourceJoint model err")
  412. }
  413. return iModel.(*SMonitorResourceAlert), nil
  414. }
  415. func (self *SMonitorResource) UpdateAlertState() error {
  416. joints, _ := MonitorResourceAlertManager.GetJoinsByListInput(monitor.MonitorResourceJointListInput{MonitorResourceId: self.ResId})
  417. jointState := monitor.MONITOR_RESOURCE_ALERT_STATUS_ATTACH
  418. if len(joints) == 0 {
  419. jointState = monitor.MONITOR_RESOURCE_ALERT_STATUS_INIT
  420. }
  421. for _, joint := range joints {
  422. if joint.AlertState == monitor.MONITOR_RESOURCE_ALERT_STATUS_ALERTING && time.Now().Sub(joint.
  423. TriggerTime) < time.Minute*30 {
  424. jointState = monitor.MONITOR_RESOURCE_ALERT_STATUS_ALERTING
  425. }
  426. }
  427. _, err := db.Update(self, func() error {
  428. self.AlertState = jointState
  429. return nil
  430. })
  431. if err != nil {
  432. return errors.Wrapf(err, "SMonitorResource:%s UpdateAlertState err", self.Name)
  433. }
  434. return nil
  435. }
  436. func (self *SMonitorResource) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  437. err := self.DetachJoint(ctx, userCred)
  438. if err != nil {
  439. return err
  440. }
  441. return self.SVirtualResourceBase.Delete(ctx, userCred)
  442. }
  443. func (self *SMonitorResource) DetachJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
  444. err := MonitorResourceAlertManager.DetachJoint(ctx, userCred,
  445. monitor.MonitorResourceJointListInput{MonitorResourceId: self.ResId})
  446. if err != nil {
  447. return errors.Wrap(err, "SMonitorResource DetachJoint err")
  448. }
  449. return nil
  450. }
  451. type AlertStatusCount struct {
  452. CountId int64
  453. AlertState string
  454. }
  455. func (manager *SMonitorResourceManager) GetPropertyAlert(ctx context.Context, userCred mcclient.TokenCredential,
  456. data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  457. scope, _ := data.GetString("scope")
  458. if len(scope) == 0 {
  459. scope = "system"
  460. }
  461. result := jsonutils.NewDict()
  462. for resType, _ := range GetResourceSyncMap() {
  463. query := manager.Query("alert_state")
  464. owner, _ := manager.FetchOwnerId(ctx, data)
  465. if owner == nil {
  466. owner = userCred
  467. }
  468. query = manager.FilterByOwner(ctx, query, manager, userCred, owner, rbacscope.TRbacScope(scope))
  469. query = query.AppendField(sqlchemy.COUNT("count_id", query.Field("id")))
  470. input := monitor.MonitorResourceListInput{ResType: resType}
  471. query = manager.FieldListFilter(query, input)
  472. query.GroupBy(query.Field("alert_state"))
  473. log.Errorf("query:%s", query.String())
  474. rows, err := query.Rows()
  475. if err != nil {
  476. return nil, errors.Wrap(err, "getMonitorResourceAlert query err")
  477. }
  478. defer rows.Close()
  479. total := int64(0)
  480. resTypeDict := jsonutils.NewDict()
  481. for rows.Next() {
  482. row := new(AlertStatusCount)
  483. err := query.Row2Struct(rows, row)
  484. if err != nil {
  485. return nil, errors.Wrap(err, "MonitorResource Row2Struct err")
  486. }
  487. resTypeDict.Add(jsonutils.NewInt(row.CountId), row.AlertState)
  488. total += row.CountId
  489. }
  490. resTypeDict.Add(jsonutils.NewInt(total), "total")
  491. result.Add(resTypeDict, resType)
  492. }
  493. return result, nil
  494. }
  495. func (manager *SMonitorResourceManager) UpdateMonitorResourceAttachJointByRecord(ctx context.Context, userCred mcclient.TokenCredential, record *SAlertRecord) error {
  496. matches, _ := record.GetEvalData()
  497. input := &UpdateMonitorResourceAlertInput{
  498. AlertId: record.AlertId,
  499. Matches: matches,
  500. ResType: record.ResType,
  501. AlertState: record.State,
  502. SendState: record.SendState,
  503. TriggerTime: record.CreatedAt,
  504. AlertRecordId: record.GetId(),
  505. }
  506. if err := manager.UpdateMonitorResourceAttachJoint(ctx, userCred, input); err != nil {
  507. return errors.Wrap(err, "UpdateMonitorResourceAttachJoint")
  508. }
  509. return nil
  510. }
  511. type UpdateMonitorResourceAlertInput struct {
  512. AlertId string
  513. Matches []monitor.EvalMatch
  514. ResType string
  515. AlertState string
  516. SendState string
  517. TriggerTime time.Time
  518. AlertRecordId string
  519. }
  520. func (manager *SMonitorResourceManager) UpdateMonitorResourceAttachJoint(ctx context.Context, userCred mcclient.TokenCredential, input *UpdateMonitorResourceAlertInput) error {
  521. resType := input.ResType
  522. if resType == monitor.METRIC_RES_TYPE_AGENT {
  523. resType = monitor.METRIC_RES_TYPE_GUEST
  524. }
  525. matches := input.Matches
  526. errs := make([]error, 0)
  527. matchResourceIds := make([]string, 0)
  528. for _, match := range matches {
  529. resId := monitor.GetMeasurementResourceId(match.Tags, input.ResType)
  530. if len(resId) == 0 {
  531. continue
  532. }
  533. matchResourceIds = append(matchResourceIds, resId)
  534. monitorResources, err := manager.GetMonitorResources(monitor.MonitorResourceListInput{ResType: resType, ResId: []string{resId}})
  535. if err != nil {
  536. errs = append(errs, errors.Wrapf(err, "SMonitorResourceManager GetMonitorResources by resId:%s err", resId))
  537. continue
  538. }
  539. for _, res := range monitorResources {
  540. err := res.UpdateAttachJoint(ctx, userCred, input, match)
  541. if err != nil {
  542. errs = append(errs, errors.Wrap(err, "UpdateAttachJoint"))
  543. }
  544. }
  545. }
  546. resourceAlerts, err := MonitorResourceAlertManager.GetJoinsByListInput(monitor.MonitorResourceJointListInput{
  547. AlertId: input.AlertId,
  548. AlertState: input.AlertState,
  549. })
  550. if err != nil {
  551. return errors.Wrapf(err, "get monitor_resource_joint by alertId: %s", input.AlertId)
  552. }
  553. deleteJointIds := make([]int64, 0)
  554. for _, joint := range resourceAlerts {
  555. metricName := joint.Metric
  556. isMetricFound := false
  557. for _, match := range matches {
  558. if match.Metric == metricName {
  559. isMetricFound = true
  560. break
  561. }
  562. }
  563. if utils.IsInStringArray(joint.MonitorResourceId, matchResourceIds) && isMetricFound {
  564. continue
  565. }
  566. deleteJointIds = append(deleteJointIds, joint.RowId)
  567. }
  568. if len(deleteJointIds) != 0 {
  569. err = MonitorResourceAlertManager.DetachJoint(ctx, userCred, monitor.MonitorResourceJointListInput{JointId: deleteJointIds})
  570. if err != nil {
  571. return errors.Wrapf(err, "DetachJoint by alertId:%s err", input.AlertId)
  572. }
  573. }
  574. return errors.NewAggregate(errs)
  575. }
  576. func (self *SMonitorResource) UpdateAttachJoint(ctx context.Context, userCred mcclient.TokenCredential, input *UpdateMonitorResourceAlertInput, match monitor.EvalMatch) error {
  577. joints, err := MonitorResourceAlertManager.GetJoinsByListInput(
  578. monitor.MonitorResourceJointListInput{
  579. MonitorResourceId: self.ResId,
  580. AlertId: input.AlertId,
  581. Metric: match.Metric,
  582. })
  583. if err != nil {
  584. return errors.Wrapf(err, "SMonitorResource: %s(%s) get joints by monitorResourceId %q , metric %q and alertId %q", self.Name, self.Id, self.ResId, match.Metric, input.AlertId)
  585. }
  586. errs := make([]error, 0)
  587. updateJoints := make([]SMonitorResourceAlert, 0)
  588. for _, joint := range joints {
  589. if joint.Metric == match.Metric {
  590. tmpJoint := joint
  591. updateJoints = append(updateJoints, tmpJoint)
  592. }
  593. }
  594. // 报警时发现没有进行关联,增加attach
  595. if len(updateJoints) == 0 {
  596. newJoint, err := self.AttachAlert(ctx, userCred, input.AlertId, match.Metric, match)
  597. if err != nil {
  598. log.Errorf("attach alert error: %s", err)
  599. }
  600. log.Infof("Attach Alert joint: %#v, match: %s", newJoint, jsonutils.Marshal(match))
  601. if err := newJoint.UpdateAlertRecordData(ctx, userCred, input, &match); err != nil {
  602. errs = append(errs, errors.Wrapf(err, "new joint %s:%s %s:%s UpdateAlertRecordData err",
  603. MonitorResourceAlertManager.GetMasterFieldName(), self.ResId,
  604. MonitorResourceAlertManager.GetSlaveFieldName(), input.AlertId))
  605. }
  606. } else {
  607. for _, joint := range updateJoints {
  608. err := joint.UpdateAlertRecordData(ctx, userCred, input, &match)
  609. if err != nil {
  610. errs = append(errs, errors.Wrapf(err, "joint %s:%s %s:%s UpdateAlertRecordData err",
  611. MonitorResourceAlertManager.GetMasterFieldName(), self.ResId,
  612. MonitorResourceAlertManager.GetSlaveFieldName(), input.AlertId))
  613. }
  614. }
  615. }
  616. if err := self.UpdateAlertState(); err != nil {
  617. errs = append(errs, errors.Wrapf(err, "UpdateAlertState"))
  618. }
  619. return errors.NewAggregate(errs)
  620. }
  621. func (manager *SMonitorResourceManager) GetResourceObj(id string) (bool, jsonutils.JSONObject) {
  622. for _, set := range manager.GetModelSets().ModelSetList() {
  623. setRv := reflect.ValueOf(set)
  624. mRv := setRv.MapIndex(reflect.ValueOf(id))
  625. if mRv.IsValid() {
  626. return true, jsonutils.Marshal(mRv.Interface())
  627. }
  628. }
  629. return false, nil
  630. }
  631. func (manager *SMonitorResourceManager) GetResourceObjByResType(typ string) (bool, []jsonutils.JSONObject) {
  632. manager.GetModelSets()
  633. for _, set := range manager.GetModelSets().ModelSetList() {
  634. if _, ok := set.(IMonitorResModelSet); !ok {
  635. continue
  636. }
  637. if set.(IMonitorResModelSet).GetResType() != typ {
  638. continue
  639. }
  640. setRv := reflect.ValueOf(set)
  641. objects := make([]jsonutils.JSONObject, 0)
  642. for _, kRv := range setRv.MapKeys() {
  643. mRv := setRv.MapIndex(kRv)
  644. objects = append(objects, jsonutils.Marshal(mRv.Interface()))
  645. }
  646. return true, objects
  647. }
  648. return false, nil
  649. }
  650. func (manager *SMonitorResourceManager) SyncManually(ctx context.Context) {
  651. manager.apih.RunManually(ctx)
  652. }
  653. func (manager *SMonitorResourceManager) SyncResources(ctx context.Context, mss *MonitorResModelSets) error {
  654. userCred := auth.AdminCredential()
  655. errs := make([]error, 0)
  656. log.Infof("start sync monitorresource")
  657. aliveIds := make([]string, 0)
  658. for _, set := range mss.ModelSetList() {
  659. setRv := reflect.ValueOf(set)
  660. needSync, typ := manager.GetSetType(set)
  661. log.Infof("Type: %s, length: %d", typ, len(setRv.MapKeys()))
  662. if !needSync {
  663. log.Infof("Type: %s don't need sync", typ)
  664. continue
  665. }
  666. for _, kRv := range setRv.MapKeys() {
  667. mRv := setRv.MapIndex(kRv)
  668. //log.Errorf("resID:%s", kRv.String())
  669. input := monitor.MonitorResourceListInput{
  670. ResId: []string{kRv.String()},
  671. }
  672. res, err := MonitorResourceManager.GetMonitorResources(input)
  673. if err != nil {
  674. return errors.Wrapf(err, "GetMonitorResources by input: %s", jsonutils.Marshal(input).String())
  675. }
  676. if mRv.IsValid() {
  677. aliveIds = append(aliveIds, kRv.String())
  678. obj := jsonutils.Marshal(mRv.Interface())
  679. if len(res) == 0 {
  680. // no find to create
  681. createData := newMonitorResourceCreateInput(obj, typ)
  682. _, err = db.DoCreate(MonitorResourceManager, ctx, userCred, nil, createData,
  683. userCred)
  684. if err != nil {
  685. name, _ := createData.GetString("name")
  686. errs = append(errs, errors.Wrapf(err, "monitorResource:%s resType:%s DoCreate err", name, typ))
  687. }
  688. continue
  689. }
  690. _, err = db.Update(&res[0], func() error {
  691. obj.(*jsonutils.JSONDict).Remove("id")
  692. (&res[0]).ResType = typ
  693. obj.Unmarshal(&res[0])
  694. return nil
  695. })
  696. if err != nil {
  697. errs = append(errs, errors.Wrapf(err, "monitorResource:%s Update err", res[0].Name))
  698. continue
  699. }
  700. res[0].PostUpdate(ctx, userCred, jsonutils.NewDict(), newMonitorResourceCreateInput(obj, typ))
  701. continue
  702. }
  703. if len(res) != 0 {
  704. log.Infof("delete monitor resource,resId: %s,resType: %s", res[0].ResId, res[0].ResType)
  705. err := (&res[0]).RealDelete(ctx, userCred)
  706. if err != nil {
  707. errs = append(errs, errors.Wrapf(err, "delete monitorResource:%s err", res[0].GetId()))
  708. }
  709. }
  710. }
  711. err := manager.DeleteMonitorResources(ctx, userCred, SdeleteRes{notIn: aliveIds, resType: typ})
  712. if err != nil {
  713. return err
  714. }
  715. }
  716. log.Infof("SMonitorResourceManager SyncResources End")
  717. err := CommonAlertManager.Run(ctx)
  718. if err != nil {
  719. log.Errorf("CommonAlertManager UpdateMonitorResourceJoint err:%v", err)
  720. }
  721. return errors.NewAggregate(errs)
  722. }
  723. func (manager *SMonitorResourceManager) GetSetType(set apihelper.IModelSet) (bool, string) {
  724. if iset, ok := set.(IMonitorResModelSet); ok {
  725. return iset.NeedSync(), iset.GetResType()
  726. }
  727. return false, "NONE"
  728. }
  729. func newMonitorResourceCreateInput(input jsonutils.JSONObject, typ string) jsonutils.JSONObject {
  730. monitorResource := jsonutils.DeepCopy(input).(*jsonutils.JSONDict)
  731. id, _ := monitorResource.GetString("id")
  732. monitorResource.Add(jsonutils.NewString(id), "res_id")
  733. monitorResource.Remove("id")
  734. monitorResource.Add(jsonutils.NewString(typ), "res_type")
  735. if monitorResource.Contains("metadata") {
  736. metadata, _ := monitorResource.Get("metadata")
  737. monitorResource.Add(metadata, "__meta__")
  738. }
  739. return monitorResource
  740. }
  741. type MonitorResourceDoActionF func(obj *SMonitorResource, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *monitor.MonitorResourceDoActionInput) (jsonutils.JSONObject, error)
  742. var (
  743. monitorResourceDoActionMap = make(map[string]MonitorResourceDoActionF)
  744. )
  745. func RegisterMonitorResourceDoAction(action string, f MonitorResourceDoActionF) {
  746. if _, ok := monitorResourceDoActionMap[action]; ok {
  747. log.Fatalf("action %s already registered for monitor resource do action", action)
  748. }
  749. monitorResourceDoActionMap[action] = f
  750. }
  751. func (res *SMonitorResource) PerformDoAction(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *monitor.MonitorResourceDoActionInput) (jsonutils.JSONObject, error) {
  752. f, ok := monitorResourceDoActionMap[input.Action]
  753. if !ok {
  754. return nil, errors.Errorf("action %q not found for monitor resource do action", input.Action)
  755. }
  756. return f(res, ctx, userCred, query, input)
  757. }