alertpannel.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. "time"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/pkg/util/rbacscope"
  21. "yunion.io/x/pkg/utils"
  22. "yunion.io/x/sqlchemy"
  23. "yunion.io/x/onecloud/pkg/apis/monitor"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. merrors "yunion.io/x/onecloud/pkg/monitor/errors"
  28. "yunion.io/x/onecloud/pkg/monitor/validators"
  29. "yunion.io/x/onecloud/pkg/util/stringutils2"
  30. )
  31. var (
  32. AlertPanelManager *SAlertPanelManager
  33. )
  34. func init() {
  35. AlertPanelManager = &SAlertPanelManager{
  36. SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
  37. SAlertPanel{},
  38. "alertpanel_tbl",
  39. "alertpanel",
  40. "alertpanels",
  41. ),
  42. }
  43. AlertPanelManager.SetVirtualObject(AlertPanelManager)
  44. }
  45. // +onecloud:swagger-gen-model-singular=alertpanel
  46. // +onecloud:swagger-gen-model-plural=alertpanels
  47. type SAlertPanelManager struct {
  48. db.SStatusStandaloneResourceBaseManager
  49. db.SScopedResourceBaseManager
  50. }
  51. type SAlertPanel struct {
  52. db.SStatusStandaloneResourceBase
  53. db.SScopedResourceBase
  54. Settings *monitor.AlertSetting `nullable:"false" list:"user" create:"required" update:"user"`
  55. Message string `charset:"utf8" list:"user" create:"optional" update:"user"`
  56. }
  57. func (manager *SAlertPanelManager) NamespaceScope() rbacscope.TRbacScope {
  58. return rbacscope.ScopeSystem
  59. }
  60. func (manager *SAlertPanelManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential,
  61. keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  62. q, err := manager.SStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  63. if err != nil {
  64. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemExportKeys")
  65. }
  66. q, err = manager.SScopedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  67. if err != nil {
  68. return nil, errors.Wrap(err, "SScopedResourceBaseManager.ListItemExportKeys")
  69. }
  70. return q, nil
  71. }
  72. func (man *SAlertPanelManager) OrderByExtraFields(
  73. ctx context.Context,
  74. q *sqlchemy.SQuery,
  75. userCred mcclient.TokenCredential,
  76. input monitor.AlertPanelListInput,
  77. ) (*sqlchemy.SQuery, error) {
  78. var err error
  79. q, err = man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.StatusStandaloneResourceListInput)
  80. if err != nil {
  81. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
  82. }
  83. q, err = man.SScopedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.ScopedResourceBaseListInput)
  84. if err != nil {
  85. return nil, errors.Wrap(err, "SScopedResourceBaseManager.OrderByExtraFields")
  86. }
  87. return q, nil
  88. }
  89. func (man *SAlertPanelManager) ValidateCreateData(
  90. ctx context.Context, userCred mcclient.TokenCredential,
  91. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
  92. data monitor.AlertPanelCreateInput) (monitor.AlertPanelCreateInput, error) {
  93. if len(data.DashboardId) == 0 {
  94. return data, httperrors.NewInputParameterError("dashboard_id is empty")
  95. } else {
  96. _, err := AlertDashBoardManager.getDashboardByid(data.DashboardId)
  97. if err != nil {
  98. return data, httperrors.NewInputParameterError("can not find dashboard:%s", data.DashboardId)
  99. }
  100. }
  101. if len(data.CommonMetricInputQuery.MetricQuery) == 0 {
  102. return data, merrors.NewArgIsEmptyErr("metric_query")
  103. } else {
  104. for _, query := range data.CommonMetricInputQuery.MetricQuery {
  105. if len(query.Comparator) != 0 {
  106. if !utils.IsInStringArray(string(getQueryEvalType(query.Comparator)), validators.EvaluatorDefaultTypes) {
  107. return data, httperrors.NewInputParameterError("the Comparator is illegal: %s", query.Comparator)
  108. }
  109. }
  110. if len(query.Reduce) != 0 {
  111. if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok {
  112. return data, httperrors.NewInputParameterError("the reduce is illegal %s", query.Reduce)
  113. }
  114. }
  115. }
  116. err := CommonAlertManager.ValidateMetricQuery(&data.CommonMetricInputQuery, data.Scope, ownerId, false)
  117. if err != nil {
  118. return data, errors.Wrap(err, "metric query error")
  119. }
  120. }
  121. //name, err := db.GenerateName(man, ownerId, data.Name)
  122. //if err != nil {
  123. // return data, err
  124. //}
  125. alertCreateInput := man.toAlertCreateInput(data)
  126. data.AlertCreateInput = alertCreateInput
  127. enable := true
  128. if data.Enabled == nil {
  129. data.Enabled = &enable
  130. }
  131. //data.Name = name
  132. return data, nil
  133. }
  134. func (man *SAlertPanelManager) HasName() bool {
  135. return false
  136. }
  137. func (man *SAlertPanelManager) toAlertCreateInput(input monitor.AlertPanelCreateInput) monitor.AlertCreateInput {
  138. ret := new(monitor.AlertCreateInput)
  139. for _, metricquery := range input.CommonMetricInputQuery.MetricQuery {
  140. condition := monitor.AlertCondition{
  141. Type: "query",
  142. Query: *metricquery.AlertQuery,
  143. //Reducer: monitor.Condition{Type: metricquery.Reduce},
  144. //Evaluator: monitor.Condition{Type: getQueryEvalType(metricquery.Comparator), Params: []float64{metricquery.Threshold}},
  145. Operator: "and",
  146. }
  147. ret.Settings.Conditions = append(ret.Settings.Conditions, condition)
  148. }
  149. return *ret
  150. }
  151. func (panel *SAlertPanel) CustomizeCreate(
  152. ctx context.Context, userCred mcclient.TokenCredential,
  153. ownerId mcclient.IIdentityProvider,
  154. query jsonutils.JSONObject,
  155. data jsonutils.JSONObject,
  156. ) error {
  157. dashboardId, err := data.GetString("dashboard_id")
  158. if len(dashboardId) == 0 {
  159. return errors.Wrap(err, "panel CustomizeCreate can not get dashboard_id")
  160. }
  161. dash, _ := AlertDashBoardManager.getDashboardByid(dashboardId)
  162. panel.ProjectId = dash.ProjectId
  163. panel.DomainId = dash.DomainId
  164. return panel.attachDashboard(ctx, dashboardId)
  165. }
  166. func (panel *SAlertPanel) attachDashboard(ctx context.Context, dashboardId string) error {
  167. joint := new(SAlertDashboardPanel)
  168. joint.DashboardId = dashboardId
  169. if panel.Id == "" {
  170. panel.Id = db.DefaultUUIDGenerator()
  171. }
  172. joint.PanelId = panel.Id
  173. err := AlertDashBoardPanelManager.TableSpec().Insert(ctx, joint)
  174. if err != nil {
  175. return errors.Wrap(err, "panel attach dashboard error")
  176. }
  177. return nil
  178. }
  179. func (man *SAlertPanelManager) ListItemFilter(
  180. ctx context.Context, q *sqlchemy.SQuery,
  181. userCred mcclient.TokenCredential,
  182. query monitor.AlertPanelListInput,
  183. ) (*sqlchemy.SQuery, error) {
  184. q, err := AlertManager.ListItemFilter(ctx, q, userCred, query.AlertListInput)
  185. if err != nil {
  186. return nil, err
  187. }
  188. if len(query.DashboardId) != 0 {
  189. joinq := AlertDashBoardPanelManager.Query(AlertDashBoardPanelManager.GetSlaveFieldName()).Equals(
  190. AlertDashBoardPanelManager.GetMasterFieldName(), query.DashboardId).SubQuery()
  191. q = q.In("id", joinq)
  192. }
  193. return q, nil
  194. }
  195. func (man *SAlertPanelManager) FetchCustomizeColumns(
  196. ctx context.Context,
  197. userCred mcclient.TokenCredential,
  198. query jsonutils.JSONObject,
  199. objs []interface{},
  200. fields stringutils2.SSortedStrings,
  201. isList bool,
  202. ) []monitor.PanelDetails {
  203. rows := make([]monitor.PanelDetails, len(objs))
  204. alertRows := AlertManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  205. for i := range rows {
  206. rows[i].AlertDetails = alertRows[i]
  207. rows[i], _ = objs[i].(*SAlertPanel).GetMoreDetails(rows[i])
  208. }
  209. return rows
  210. }
  211. func (panel *SAlertPanel) GetMoreDetails(out monitor.PanelDetails) (monitor.PanelDetails, error) {
  212. if panel.Settings == nil || len(panel.Settings.Conditions) == 0 {
  213. return out, nil
  214. }
  215. out.CommonAlertMetricDetails = make([]*monitor.CommonAlertMetricDetails, len(panel.Settings.Conditions))
  216. for i, cond := range panel.Settings.Conditions {
  217. metricDetails := panel.GetCommonAlertMetricDetailsFromAlertCondition(i, &cond)
  218. out.CommonAlertMetricDetails[i] = metricDetails
  219. panel.Settings.Conditions[i] = cond
  220. }
  221. out.Settings = panel.Settings
  222. return out, nil
  223. }
  224. func (dash *SAlertPanel) GetCommonAlertMetricDetailsFromAlertCondition(index int,
  225. cond *monitor.AlertCondition) *monitor.
  226. CommonAlertMetricDetails {
  227. metricDetails := new(monitor.CommonAlertMetricDetails)
  228. getCommonAlertMetricDetailsFromCondition(cond, metricDetails)
  229. return metricDetails
  230. }
  231. func (dash *SAlertPanel) ValidateUpdateData(
  232. ctx context.Context,
  233. userCred mcclient.TokenCredential,
  234. query jsonutils.JSONObject,
  235. data *jsonutils.JSONDict,
  236. ) (*jsonutils.JSONDict, error) {
  237. updataInput := new(monitor.AlertPanelUpdateInput)
  238. if refresh, _ := data.GetString("refresh"); len(refresh) > 0 {
  239. if _, err := time.ParseDuration(refresh); err != nil {
  240. return data, httperrors.NewInputParameterError("Invalid refresh format: %s", refresh)
  241. }
  242. }
  243. if metric_query, _ := data.GetArray("metric_query"); len(metric_query) > 0 {
  244. for i, _ := range metric_query {
  245. query := new(monitor.CommonAlertQuery)
  246. err := metric_query[i].Unmarshal(query)
  247. if err != nil {
  248. return data, errors.Wrap(err, "metric_query Unmarshal error")
  249. }
  250. if len(query.Comparator) != 0 {
  251. if !utils.IsInStringArray(string(getQueryEvalType(query.Comparator)), validators.EvaluatorDefaultTypes) {
  252. return data, httperrors.NewInputParameterError("the Comparator is illegal: %s", query.Comparator)
  253. }
  254. }
  255. if len(query.Reduce) != 0 {
  256. if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok {
  257. return data, httperrors.NewInputParameterError("the reduce is illegal: %s", query.Reduce)
  258. }
  259. }
  260. }
  261. metricQuery := new(monitor.CommonMetricInputQuery)
  262. err := data.Unmarshal(metricQuery)
  263. if err != nil {
  264. return data, errors.Wrap(err, "metric_query Unmarshal error")
  265. }
  266. ownerId, _ := AlertDashBoardManager.FetchOwnerId(ctx, data)
  267. if ownerId == nil {
  268. ownerId = userCred
  269. }
  270. scope, _ := data.GetString("scope")
  271. err = CommonAlertManager.ValidateMetricQuery(metricQuery, scope, ownerId, false)
  272. if err != nil {
  273. return data, errors.Wrap(err, "metric query error")
  274. }
  275. data.Update(jsonutils.Marshal(metricQuery))
  276. err = data.Unmarshal(updataInput)
  277. if err != nil {
  278. return data, errors.Wrap(err, "updataInput Unmarshal err")
  279. }
  280. alertCreateInput := dash.getUpdateAlertInput(*updataInput)
  281. data.Set("settings", jsonutils.Marshal(&alertCreateInput.Settings))
  282. if err != nil {
  283. return data, errors.Wrap(err, "SAlertPanel.ValidateUpdateData")
  284. }
  285. data.Update(jsonutils.Marshal(updataInput))
  286. }
  287. return data, nil
  288. }
  289. func (panel *SAlertPanel) getUpdateAlertInput(updateInput monitor.AlertPanelUpdateInput) monitor.AlertCreateInput {
  290. input := monitor.AlertPanelCreateInput{
  291. CommonMetricInputQuery: updateInput.CommonMetricInputQuery,
  292. }
  293. createInput := AlertPanelManager.toAlertCreateInput(input)
  294. return createInput
  295. }
  296. func (panel *SAlertPanel) CustomizeDelete(
  297. ctx context.Context, userCred mcclient.TokenCredential,
  298. query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  299. dashboardPanel, err := panel.getPanelJoint()
  300. if err != nil {
  301. return errors.Wrap(err, "panel getPanelJoint error")
  302. }
  303. err = dashboardPanel.Detach(ctx, userCred)
  304. if err != nil {
  305. return errors.Wrap(err, "dashboardPanel do detach error")
  306. }
  307. return nil
  308. }
  309. func (panel *SAlertPanel) getPanelJoint() (*SAlertDashboardPanel, error) {
  310. iModel, err := db.NewModelObject(AlertDashBoardPanelManager)
  311. if err != nil {
  312. return nil, errors.Wrap(err, "panel NewModelObject error")
  313. }
  314. query := AlertDashBoardPanelManager.Query().Equals(AlertDashBoardPanelManager.GetSlaveFieldName(), panel.Id)
  315. err = query.First(iModel)
  316. if err != nil {
  317. return nil, errors.Wrapf(err, "alertdashboardpanelmanager exc first query by panel:%s error", panel.Id)
  318. }
  319. return iModel.(*SAlertDashboardPanel), nil
  320. }
  321. func (manager *SAlertPanelManager) getPanelByid(id string) (*SAlertPanel, error) {
  322. iModel, err := db.FetchById(manager, id)
  323. if err != nil {
  324. return nil, err
  325. }
  326. return iModel.(*SAlertPanel), nil
  327. }
  328. func (panel *SAlertPanel) ClonePanel(ctx context.Context, dashboardId string,
  329. input monitor.AlertClonePanelInput) (*SAlertPanel, error) {
  330. iModel, _ := db.NewModelObject(AlertPanelManager)
  331. panelJson := jsonutils.Marshal(panel)
  332. panelJson.Unmarshal(iModel)
  333. iModel.(*SAlertPanel).Id = ""
  334. name := input.ClonePanelName
  335. var err error
  336. if len(name) == 0 {
  337. name, err = db.GenerateName2(ctx, AlertPanelManager, nil, panel.GetName(), iModel, 1)
  338. if err != nil {
  339. return nil, errors.Wrap(err, "clonePanel GenerateName err")
  340. }
  341. }
  342. iModel.(*SAlertPanel).Name = name
  343. err = AlertPanelManager.TableSpec().Insert(ctx, iModel)
  344. if err != nil {
  345. return nil, errors.Wrapf(err, "panel:%s clone err", panel.Id)
  346. }
  347. iModel.(*SAlertPanel).attachDashboard(ctx, dashboardId)
  348. return iModel.(*SAlertPanel), nil
  349. }