From c66ea1145ddf43dc77afc5958beed7254bf3095a Mon Sep 17 00:00:00 2001 From: zhaoxiangchun <39040177+zhaoxiangchun@users.noreply.github.com> Date: Tue, 1 Sep 2020 10:43:35 +0800 Subject: [PATCH] monitor commonalert bugfix (#7667) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 统一监控查询面板保存 * commonalert bugfix --- cmd/climc/shell/monitor/alertdashboard.go | 15 + cmd/climc/shell/monitor/commonalertmetric.go | 1 + .../shell/monitor/commonalertmetricfield.go | 15 + pkg/apis/monitor/alertdashboard.go | 29 ++ pkg/hostman/hostmetrics/hostmetrics.go | 28 +- pkg/mcclient/modules/mod_alert_dashboard.go | 24 ++ .../modules/mod_commonalert_metric_field.go | 25 ++ .../options/monitor/alertdashboard.go | 88 +++++ .../options/monitor/commonalertmetric.go | 12 + .../options/monitor/commonalertmetricfield.go | 61 ++++ pkg/monitor/dbinit/metric_dbinit.go | 5 - pkg/monitor/models/alertdashboard.go | 302 ++++++++++++++++++ pkg/monitor/models/commonalert.go | 26 +- pkg/monitor/models/datasource.go | 43 ++- pkg/monitor/models/metric.go | 34 ++ pkg/monitor/models/metric_field.go | 48 +++ pkg/monitor/models/metric_joint.go | 9 + pkg/monitor/models/suggestsysrule.go | 29 +- pkg/monitor/models/unifiedmonitor.go | 107 ++++--- pkg/monitor/service/handlers.go | 1 + 20 files changed, 820 insertions(+), 82 deletions(-) create mode 100644 cmd/climc/shell/monitor/alertdashboard.go create mode 100644 cmd/climc/shell/monitor/commonalertmetricfield.go create mode 100644 pkg/apis/monitor/alertdashboard.go create mode 100644 pkg/mcclient/modules/mod_alert_dashboard.go create mode 100644 pkg/mcclient/modules/mod_commonalert_metric_field.go create mode 100644 pkg/mcclient/options/monitor/alertdashboard.go create mode 100644 pkg/mcclient/options/monitor/commonalertmetricfield.go create mode 100644 pkg/monitor/models/alertdashboard.go diff --git a/cmd/climc/shell/monitor/alertdashboard.go b/cmd/climc/shell/monitor/alertdashboard.go new file mode 100644 index 0000000000..1153d6fc67 --- /dev/null +++ b/cmd/climc/shell/monitor/alertdashboard.go @@ -0,0 +1,15 @@ +package monitor + +import ( + "yunion.io/x/onecloud/cmd/climc/shell" + "yunion.io/x/onecloud/pkg/mcclient/modules" + options "yunion.io/x/onecloud/pkg/mcclient/options/monitor" +) + +func init() { + cmd := shell.NewResourceCmd(modules.AlertDashBoardManager) + cmd.Create(new(options.AlertDashBoardCreateOptions)) + cmd.List(new(options.AlertDashBoardListOptions)) + cmd.Show(new(options.AlertDashBoardShowOptions)) + cmd.Delete(new(options.AlertDashBoardDeleteOptions)) +} diff --git a/cmd/climc/shell/monitor/commonalertmetric.go b/cmd/climc/shell/monitor/commonalertmetric.go index b6e89d7326..da94811167 100644 --- a/cmd/climc/shell/monitor/commonalertmetric.go +++ b/cmd/climc/shell/monitor/commonalertmetric.go @@ -11,4 +11,5 @@ func init() { cmd.List(new(options.MonitorMetricListOptions)) cmd.Update(new(options.MetricUpdateOptions)) cmd.Show(new(options.MetricShowOptions)) + cmd.Delete(new(options.MetricDeleteOptions)) } diff --git a/cmd/climc/shell/monitor/commonalertmetricfield.go b/cmd/climc/shell/monitor/commonalertmetricfield.go new file mode 100644 index 0000000000..cdd4059769 --- /dev/null +++ b/cmd/climc/shell/monitor/commonalertmetricfield.go @@ -0,0 +1,15 @@ +package monitor + +import ( + "yunion.io/x/onecloud/cmd/climc/shell" + "yunion.io/x/onecloud/pkg/mcclient/modules" + options "yunion.io/x/onecloud/pkg/mcclient/options/monitor" +) + +func init() { + cmd := shell.NewResourceCmd(modules.MetricFieldManager) + cmd.List(new(options.MonitorMetricFieldListOptions)) + cmd.Update(new(options.MetricFieldUpdateOptions)) + cmd.Show(new(options.MetricFieldShowOptions)) + cmd.Delete(new(options.MetricFieldDeleteOptions)) +} diff --git a/pkg/apis/monitor/alertdashboard.go b/pkg/apis/monitor/alertdashboard.go new file mode 100644 index 0000000000..ffadb41937 --- /dev/null +++ b/pkg/apis/monitor/alertdashboard.go @@ -0,0 +1,29 @@ +package monitor + +import "yunion.io/x/onecloud/pkg/apis" + +type AlertDashBoardCreateInput struct { + apis.ScopedResourceInput + + CommonMetricInputQuery + AlertCreateInput + + Refresh string `json:"refresh"` +} + +type AlertDashBoardListInput struct { + AlertListInput +} + +type AlertDashBoardDetails struct { + AlertDetails + + CommonAlertMetricDetails []*CommonAlertMetricDetails `json:"common_alert_metric_details"` +} + +type AlertDashBoardUpdateInput struct { + CommonMetricInputQuery + V1AlertUpdateInput + + Refresh string `json:"refresh"` +} diff --git a/pkg/hostman/hostmetrics/hostmetrics.go b/pkg/hostman/hostmetrics/hostmetrics.go index 7065e5dc27..518fb04e23 100644 --- a/pkg/hostman/hostmetrics/hostmetrics.go +++ b/pkg/hostman/hostmetrics/hostmetrics.go @@ -192,6 +192,8 @@ func (s *SGuestMonitorCollector) GetGuests() map[string]*SGuestMonitor { gm.ScalingGroupId, _ = guest.Desc.GetString("scaling_group_id") gm.Tenant, _ = guest.Desc.GetString("tenant") gm.TenantId, _ = guest.Desc.GetString("tenant_id") + gm.DomainId, _ = guest.Desc.GetString("domain_id") + gm.ProjectDomain, _ = guest.Desc.GetString("project_domain") gms[guestId] = gm } @@ -227,20 +229,22 @@ func (s *SGuestMonitorCollector) toTelegrafReportData(data *jsonutils.JSONDict) ret := []string{} vs, _ := data.GetMap() for guestId, report := range vs { - var vmName, vmIp, scalingGroupId, tenant, tenantId string + var vmName, vmIp, scalingGroupId, tenant, tenantId, domainId, projectDomain string if gm, ok := s.monitors[guestId]; ok { vmName = gm.Name vmIp = gm.Ip scalingGroupId = gm.ScalingGroupId tenant = gm.Tenant tenantId = gm.TenantId + domainId = gm.DomainId + projectDomain = gm.ProjectDomain } rs, _ := report.(*jsonutils.JSONDict).GetMap() for metrics, stat := range rs { tags := map[string]string{ "vm_id": guestId, "vm_name": vmName, "vm_ip": vmIp, - "is_vm": "true", "brand": "OneCloud", + "is_vm": "true", "brand": "OneCloud", "res_type": "guest", } if len(scalingGroupId) > 0 { tags["vm_scaling_group_id"] = scalingGroupId @@ -251,6 +255,12 @@ func (s *SGuestMonitorCollector) toTelegrafReportData(data *jsonutils.JSONDict) if len(tenantId) > 0 { tags["tenant_id"] = tenantId } + if len(domainId) > 0 { + tags["domain_id"] = domainId + } + if len(projectDomain) > 0 { + tags["project_domain"] = projectDomain + } if val, ok := stat.(*jsonutils.JSONDict); ok { line := s.addTelegrafLine(metrics, tags, val) ret = append(ret, line) @@ -335,14 +345,15 @@ func (s *SGuestMonitorCollector) collectGmReport( diskio1, err1 := gmData.Get(gmDiskio) diskio2, err2 := prevUsage.Get(gmDiskio) if err1 == nil && err2 == nil { - s.addDiskio(diskio1, diskio2, []string{"read_bytes", "write_bytes"}) + s.addDiskio(diskio1, diskio2, []string{"read_bytes", "write_bytes", "read_bits", "write_bits", "read_count", + "write_count"}) } return gmData } func (s *SGuestMonitorCollector) GetIoFiledName(field string) string { kmap := map[string]string{ - "bits": "bps", "bytes": "bps", "packets": "pps", + "bits": "bps", "bytes": "Bps", "packets": "pps", "count": "iops", } for k, v := range kmap { if strings.Contains(field, k) { @@ -414,6 +425,8 @@ type SGuestMonitor struct { ScalingGroupId string Tenant string TenantId string + DomainId string + ProjectDomain string } func NewGuestMonitor(name, id string, pid int, nics []jsonutils.JSONObject, cpuCount int, @@ -426,7 +439,7 @@ func NewGuestMonitor(name, id string, pid int, nics []jsonutils.JSONObject, cpuC if err != nil { return nil, err } - return &SGuestMonitor{name, id, pid, nics, cpuCount, ip, proc, "", "", ""}, nil + return &SGuestMonitor{name, id, pid, nics, cpuCount, ip, proc, "", "", "", "", ""}, nil } func (m *SGuestMonitor) UpdateVmName(name string) { @@ -501,6 +514,7 @@ func (m *SGuestMonitor) Cpu() jsonutils.JSONObject { percent, _ := m.Process.Percent(time.Millisecond * 100) cpuTimes, _ := m.Process.Times() ret := jsonutils.NewDict() + percent, _ = strconv.ParseFloat(fmt.Sprintf("%0.4f", percent/float64(m.CpuCnt)), 64) ret.Set("usage_active", jsonutils.NewFloat(percent)) ret.Set("cpu_usage_idle_pcore", jsonutils.NewFloat(100-percent/float64(m.CpuCnt))) ret.Set("cpu_usage_pcore", jsonutils.NewFloat(percent/float64(m.CpuCnt))) @@ -527,6 +541,10 @@ func (m *SGuestMonitor) Diskio() jsonutils.JSONObject { ret.Set("meta", meta) ret.Set("read_bytes", jsonutils.NewInt(int64(io.ReadBytes))) ret.Set("write_bytes", jsonutils.NewInt(int64(io.WriteBytes))) + ret.Set("read_bits", jsonutils.NewInt(int64(io.ReadBytes)*8)) + ret.Set("write_bits", jsonutils.NewInt(int64(io.WriteBytes)*8)) + ret.Set("read_count", jsonutils.NewInt(int64(io.ReadCount))) + ret.Set("write_count", jsonutils.NewInt(int64(io.WriteCount))) return ret } diff --git a/pkg/mcclient/modules/mod_alert_dashboard.go b/pkg/mcclient/modules/mod_alert_dashboard.go new file mode 100644 index 0000000000..a76a92385c --- /dev/null +++ b/pkg/mcclient/modules/mod_alert_dashboard.go @@ -0,0 +1,24 @@ +package modules + +import "yunion.io/x/onecloud/pkg/mcclient/modulebase" + +type SAlertDashBoardManager struct { + *modulebase.ResourceManager +} + +var ( + AlertDashBoardManager *SAlertDashBoardManager +) + +func init() { + AlertDashBoardManager = NewAlertDashBoardManager() + register(AlertDashBoardManager) +} +func NewAlertDashBoardManager() *SAlertDashBoardManager { + man := NewMonitorV2Manager("alertdashboard", "alertdashboards", + []string{"id", "name", "refresh", "common_alert_metric_details"}, + []string{}) + return &SAlertDashBoardManager{ + ResourceManager: &man, + } +} diff --git a/pkg/mcclient/modules/mod_commonalert_metric_field.go b/pkg/mcclient/modules/mod_commonalert_metric_field.go new file mode 100644 index 0000000000..0a9be1c2bf --- /dev/null +++ b/pkg/mcclient/modules/mod_commonalert_metric_field.go @@ -0,0 +1,25 @@ +package modules + +import "yunion.io/x/onecloud/pkg/mcclient/modulebase" + +type SMetricFieldManager struct { + *modulebase.ResourceManager +} + +var ( + MetricFieldManager *SMetricFieldManager +) + +func init() { + MetricFieldManager = NewMetricFieldManager() + register(MetricFieldManager) +} + +func NewMetricFieldManager() *SMetricFieldManager { + man := NewMonitorV2Manager("metricfield", "metricfields", + []string{"id", "name", "display_name", "Unit"}, + []string{}) + return &SMetricFieldManager{ + ResourceManager: &man, + } +} diff --git a/pkg/mcclient/options/monitor/alertdashboard.go b/pkg/mcclient/options/monitor/alertdashboard.go new file mode 100644 index 0000000000..65d7ba56d9 --- /dev/null +++ b/pkg/mcclient/options/monitor/alertdashboard.go @@ -0,0 +1,88 @@ +package monitor + +import ( + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/apis/monitor" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type AlertDashBoardCreateOptions struct { + NAME string `help:"Name of bashboard"` + Metric string `help:"Metric name, include measurement and field, e.g. vm_cpu.usage_active" required:"true"` + Database string + Interval string `help:"query aggregation interval e.g. 1m|5s"` + From string `help:"query start time e.g. 5m|6h"` + To string `help:"query end time"` + Refresh string `help:"dashboard query refresh priod e.g. 1m|5m"` + Scope string +} + +func (o *AlertDashBoardCreateOptions) Params() (jsonutils.JSONObject, error) { + createInput := new(monitor.AlertDashBoardCreateInput) + createInput.Name = o.NAME + createInput.Scope = o.Scope + createInput.Refresh = o.Refresh + createInput.From = o.From + createInput.To = o.To + alertQuery := new(monitor.CommonAlertQuery) + metrics := strings.Split(o.Metric, ".") + if len(metrics) != 2 { + return nil, errors.Wrap(httperrors.ErrBadRequest, "metric") + } + measurement := metrics[0] + field := metrics[1] + sels := make([]monitor.MetricQuerySelect, 0) + sels = append(sels, monitor.NewMetricQuerySelect( + monitor.MetricQueryPart{ + Type: "field", + Params: []string{field}, + })) + q := monitor.MetricQuery{ + Database: o.Database, + Measurement: measurement, + Selects: sels, + } + tmp := new(monitor.AlertQuery) + tmp.Model = q + alertQuery.AlertQuery = tmp + createInput.MetricQuery = make([]*monitor.CommonAlertQuery, 0) + createInput.MetricQuery = append(createInput.MetricQuery, alertQuery) + return jsonutils.Marshal(createInput), nil +} + +type AlertDashBoardListOptions struct { + options.BaseListOptions +} + +func (o *AlertDashBoardListOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(o) +} + +type AlertDashBoardShowOptions struct { + ID string `help:"ID of Metric " json:"-"` +} + +func (o *AlertDashBoardShowOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(o) +} + +func (o *AlertDashBoardShowOptions) GetId() string { + return o.ID +} + +type AlertDashBoardDeleteOptions struct { + ID string `json:"-"` +} + +func (o *AlertDashBoardDeleteOptions) GetId() string { + return o.ID +} + +func (o *AlertDashBoardDeleteOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(o) +} diff --git a/pkg/mcclient/options/monitor/commonalertmetric.go b/pkg/mcclient/options/monitor/commonalertmetric.go index f8c0cb8a61..73b0214c8e 100644 --- a/pkg/mcclient/options/monitor/commonalertmetric.go +++ b/pkg/mcclient/options/monitor/commonalertmetric.go @@ -71,3 +71,15 @@ func (o *MetricShowOptions) Params() (jsonutils.JSONObject, error) { func (o *MetricShowOptions) GetId() string { return o.ID } + +type MetricDeleteOptions struct { + ID string `help:"ID of Metric " json:"-"` +} + +func (o *MetricDeleteOptions) GetId() string { + return o.ID +} + +func (o *MetricDeleteOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(o) +} diff --git a/pkg/mcclient/options/monitor/commonalertmetricfield.go b/pkg/mcclient/options/monitor/commonalertmetricfield.go new file mode 100644 index 0000000000..4733e8c339 --- /dev/null +++ b/pkg/mcclient/options/monitor/commonalertmetricfield.go @@ -0,0 +1,61 @@ +package monitor + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type MonitorMetricFieldListOptions struct { + options.BaseListOptions + Names []string `help:"name of field"` + Unit string `help:"Unit of Field " choices:"%|bps|Mbps|Bps|cps|count|ms|byte"` + DisplayName string `help:"The name of the field customization"` +} + +func (o *MonitorMetricFieldListOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(o) +} + +type MetricFieldUpdateOptions struct { + ID string `help:"ID of Metric " required:"true" positional:"true"` + DisplayName string `help:"The name of the field customization" required:"true"` + Name string `help:"Name of Field" required:"true"` + Unit string `help:"Unit of Field" choices:"%|bps|Mbps|Bps|cps|count|ms|byte" required:"true"` +} + +func (o *MetricFieldUpdateOptions) GetId() string { + return o.ID +} + +func (o *MetricFieldUpdateOptions) Params() (jsonutils.JSONObject, error) { + params, err := options.StructToParams(o) + if err != nil { + return nil, err + } + return params, nil +} + +type MetricFieldShowOptions struct { + ID string `help:"ID of Metric " json:"-"` +} + +func (o *MetricFieldShowOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(o) +} + +func (o *MetricFieldShowOptions) GetId() string { + return o.ID +} + +type MetricFieldDeleteOptions struct { + ID string `help:"ID of Metric " json:"-"` +} + +func (o *MetricFieldDeleteOptions) GetId() string { + return o.ID +} + +func (o *MetricFieldDeleteOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(o) +} diff --git a/pkg/monitor/dbinit/metric_dbinit.go b/pkg/monitor/dbinit/metric_dbinit.go index c356358b9a..e545f99bd4 100644 --- a/pkg/monitor/dbinit/metric_dbinit.go +++ b/pkg/monitor/dbinit/metric_dbinit.go @@ -295,11 +295,6 @@ var MetricDescriptions = ` "database":"telegraf" }, "metric_fields": [ - { - "name":"cpu_count", - "display_name":"CPU cores", - "unit":"count" - }, { "name":"cpu_time_system", "display_name":"CPU system state time", diff --git a/pkg/monitor/models/alertdashboard.go b/pkg/monitor/models/alertdashboard.go new file mode 100644 index 0000000000..521ffba4ec --- /dev/null +++ b/pkg/monitor/models/alertdashboard.go @@ -0,0 +1,302 @@ +package models + +import ( + "context" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/apis/monitor" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/monitor/validators" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SAlertDashBoardManager struct { + db.SEnabledResourceBaseManager + db.SStatusStandaloneResourceBaseManager + db.SScopedResourceBaseManager +} + +type SAlertDashBoard struct { + //db.SVirtualResourceBase + db.SEnabledResourceBase + db.SStatusStandaloneResourceBase + db.SScopedResourceBase + + Refresh string `nullable:"false" list:"user" create:"required" update:"user"` + Settings jsonutils.JSONObject `nullable:"false" list:"user" create:"required" update:"user"` + Message string `charset:"utf8" list:"user" create:"optional" update:"user"` +} + +var AlertDashBoardManager *SAlertDashBoardManager + +func init() { + AlertDashBoardManager = &SAlertDashBoardManager{ + SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SAlertDashBoard{}, + "alertdashboard_tbl", + "alertdashboard", + "alertdashboards", + ), + } + + AlertDashBoardManager.SetVirtualObject(AlertDashBoardManager) +} + +func (manager *SAlertDashBoardManager) NamespaceScope() rbacutils.TRbacScope { + return rbacutils.ScopeSystem +} + +func (manager *SAlertDashBoardManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) { + q, err := manager.SStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemExportKeys") + } + q, err = manager.SScopedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SScopedResourceBaseManager.ListItemExportKeys") + } + return q, nil +} + +func (man *SAlertDashBoardManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + input monitor.AlertDashBoardListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.StatusStandaloneResourceListInput) + if err != nil { + return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields") + } + q, err = man.SScopedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.ScopedResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SScopedResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (man *SAlertDashBoardManager) ValidateCreateData( + ctx context.Context, userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, + data monitor.AlertDashBoardCreateInput) (monitor.AlertDashBoardCreateInput, error) { + if len(data.Refresh) != 0 { + if _, err := time.ParseDuration(data.Refresh); err != nil { + return data, httperrors.NewInputParameterError("Invalid refresh format: %s", data.Refresh) + } + } + if len(data.CommonMetricInputQuery.MetricQuery) == 0 { + return data, httperrors.NewInputParameterError("metric_query is empty") + } else { + for _, query := range data.CommonMetricInputQuery.MetricQuery { + if len(query.Comparator) != 0 { + if !utils.IsInStringArray(getQueryEvalType(query.Comparator), validators.EvaluatorDefaultTypes) { + return data, httperrors.NewInputParameterError("the Comparator is illegal:", query.Comparator) + } + } + if len(query.Reduce) != 0 { + if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok { + return data, httperrors.NewInputParameterError("the reduce is illegal", query.Reduce) + } + } + } + err := CommonAlertManager.ValidateMetricQuery(&data.CommonMetricInputQuery, data.Scope, ownerId) + if err != nil { + return data, errors.Wrap(err, "metric query error") + } + } + + name, err := CommonAlertManager.genName(ownerId, data.Name) + if err != nil { + return data, err + } + data.Name = name + + alertCreateInput := man.toAlertCreateInput(data) + //alertCreateInput, err = AlertManager.ValidateCreateData(ctx, userCred, ownerId, query, alertCreateInput) + //if err != nil { + // return data, err + //} + data.AlertCreateInput = alertCreateInput + enable := true + if data.Enabled == nil { + data.Enabled = &enable + } + return data, nil +} + +func (man *SAlertDashBoardManager) toAlertCreateInput(input monitor.AlertDashBoardCreateInput) monitor.AlertCreateInput { + ret := new(monitor.AlertCreateInput) + for _, metricquery := range input.CommonMetricInputQuery.MetricQuery { + condition := monitor.AlertCondition{ + Type: "query", + Query: *metricquery.AlertQuery, + Reducer: monitor.Condition{Type: metricquery.Reduce}, + Evaluator: monitor.Condition{Type: getQueryEvalType(metricquery.Comparator), Params: []float64{metricquery.Threshold}}, + Operator: "and", + } + if metricquery.FieldOpt != "" { + condition.Reducer.Operators = []string{metricquery.FieldOpt} + } + ret.Settings.Conditions = append(ret.Settings.Conditions, condition) + } + return *ret +} + +func (dash *SAlertDashBoard) CustomizeCreate( + ctx context.Context, userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + data jsonutils.JSONObject, +) error { + return dash.SScopedResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) +} + +func (man *SAlertDashBoardManager) ListItemFilter( + ctx context.Context, q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query monitor.AlertDashBoardListInput, +) (*sqlchemy.SQuery, error) { + q, err := AlertManager.ListItemFilter(ctx, q, userCred, query.AlertListInput) + if err != nil { + return nil, err + } + return q, nil +} + +func (man *SAlertDashBoardManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []monitor.AlertDashBoardDetails { + rows := make([]monitor.AlertDashBoardDetails, len(objs)) + alertRows := AlertManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + for i := range rows { + rows[i].AlertDetails = alertRows[i] + rows[i], _ = objs[i].(*SAlertDashBoard).GetMoreDetails(rows[i]) + } + return rows +} + +func (dash *SAlertDashBoard) GetMoreDetails(out monitor.AlertDashBoardDetails) (monitor.AlertDashBoardDetails, error) { + setting, err := dash.GetSettings() + if err != nil { + return out, err + } + if len(setting.Conditions) == 0 { + return out, nil + } + + out.CommonAlertMetricDetails = make([]*monitor.CommonAlertMetricDetails, len(setting.Conditions)) + for i, cond := range setting.Conditions { + metricDetails := dash.GetCommonAlertMetricDetailsFromAlertCondition(i, cond) + out.CommonAlertMetricDetails[i] = metricDetails + } + return out, nil +} + +func (dash *SAlertDashBoard) GetCommonAlertMetricDetailsFromAlertCondition(index int, + cond monitor.AlertCondition) *monitor. + CommonAlertMetricDetails { + metricDetails := new(monitor.CommonAlertMetricDetails) + getCommonAlertMetricDetailsFromCondition(cond, metricDetails) + return metricDetails +} + +func (dash *SAlertDashBoard) GetSettings() (*monitor.AlertSetting, error) { + setting := new(monitor.AlertSetting) + if dash.Settings == nil { + return setting, nil + } + if err := dash.Settings.Unmarshal(setting); err != nil { + return nil, errors.Wrapf(err, "dashboard %s unmarshal", dash.GetId()) + } + return setting, nil +} + +func (dash *SAlertDashBoard) ValidateUpdateData( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data *jsonutils.JSONDict, +) (*jsonutils.JSONDict, error) { + updataInput := new(monitor.AlertDashBoardUpdateInput) + if refresh, _ := data.GetString("refresh"); len(refresh) > 0 { + if _, err := time.ParseDuration(refresh); err != nil { + return data, httperrors.NewInputParameterError("Invalid refresh format: %s", refresh) + } + } + + if metric_query, _ := data.GetArray("metric_query"); len(metric_query) > 0 { + for i, _ := range metric_query { + query := new(monitor.CommonAlertQuery) + err := metric_query[i].Unmarshal(query) + if err != nil { + return data, errors.Wrap(err, "metric_query Unmarshal error") + } + if len(query.Comparator) != 0 { + if !utils.IsInStringArray(getQueryEvalType(query.Comparator), validators.EvaluatorDefaultTypes) { + return data, httperrors.NewInputParameterError("the Comparator is illegal:", query.Comparator) + } + } + if len(query.Reduce) != 0 { + if _, ok := monitor.AlertReduceFunc[query.Reduce]; !ok { + return data, httperrors.NewInputParameterError("the reduce is illegal", query.Reduce) + } + } + } + metricQuery := new(monitor.CommonMetricInputQuery) + err := data.Unmarshal(metricQuery) + if err != nil { + return data, errors.Wrap(err, "metric_query Unmarshal error") + } + ownerId, _ := AlertDashBoardManager.FetchOwnerId(ctx, data) + if ownerId == nil { + ownerId = userCred + } + scope, _ := data.GetString("scope") + err = CommonAlertManager.ValidateMetricQuery(metricQuery, scope, ownerId) + if err != nil { + return data, errors.Wrap(err, "metric query error") + } + + data.Update(jsonutils.Marshal(metricQuery)) + err = data.Unmarshal(updataInput) + if err != nil { + return data, errors.Wrap(err, "updataInput Unmarshal err") + } + alertCreateInput := dash.getUpdateAlertInput(*updataInput) + //alertCreateInput, err = AlertManager.ValidateCreateData(ctx, userCred, nil, query, alertCreateInput) + //if err != nil { + // return data, err + //} + data.Set("settings", jsonutils.Marshal(&alertCreateInput.Settings)) + updataInput.StandaloneResourceBaseUpdateInput, err = dash.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, + query, updataInput.StandaloneResourceBaseUpdateInput) + if err != nil { + return data, errors.Wrap(err, "SAlertDashBoard.ValidateUpdateData") + } + data.Update(jsonutils.Marshal(updataInput)) + } + return data, nil +} + +func (dash *SAlertDashBoard) getUpdateAlertInput(updateInput monitor.AlertDashBoardUpdateInput) monitor.AlertCreateInput { + input := monitor.AlertDashBoardCreateInput{ + CommonMetricInputQuery: updateInput.CommonMetricInputQuery, + } + createInput := AlertDashBoardManager.toAlertCreateInput(input) + return createInput +} diff --git a/pkg/monitor/models/commonalert.go b/pkg/monitor/models/commonalert.go index fb55ce059c..0d9df4b06d 100644 --- a/pkg/monitor/models/commonalert.go +++ b/pkg/monitor/models/commonalert.go @@ -112,7 +112,7 @@ func (man *SCommonAlertManager) ValidateCreateData( return data, httperrors.NewInputParameterError("the AlertType is illegal:%s", data.AlertType) } } - err := man.ValidateMetricQuery(&data.CommonMetricInputQuery) + var err = man.ValidateMetricQuery(&data.CommonMetricInputQuery, data.Scope, ownerId) if err != nil { return data, errors.Wrap(err, "metric query error") } @@ -142,14 +142,14 @@ func (man *SCommonAlertManager) genName(ownerId mcclient.IIdentityProvider, name return name, nil } -func (man *SCommonAlertManager) ValidateMetricQuery(metricRequest *monitor.CommonMetricInputQuery) error { +func (man *SCommonAlertManager) ValidateMetricQuery(metricRequest *monitor.CommonMetricInputQuery, scope string, ownerId mcclient.IIdentityProvider) error { for _, q := range metricRequest.MetricQuery { metriInputQuery := monitor.MetricInputQuery{ From: metricRequest.From, To: metricRequest.To, Interval: metricRequest.Interval, } - setDefaultValue(q.AlertQuery, &metriInputQuery) + setDefaultValue(q.AlertQuery, &metriInputQuery, scope, ownerId) err := UnifiedMonitorManager.ValidateInputQuery(q.AlertQuery) if err != nil { return err @@ -457,6 +457,15 @@ func (alert *SCommonAlert) GetCommonAlertMetricDetailsFromAlertCondition(index i CommonAlertMetricDetails { fieldOpt := alert.getFieldOpt() metricDetails := new(monitor.CommonAlertMetricDetails) + if fieldOpt != "" { + metricDetails.FieldOpt = strings.Split(fieldOpt, "+")[index] + } + getCommonAlertMetricDetailsFromCondition(cond, metricDetails) + return metricDetails +} + +func getCommonAlertMetricDetailsFromCondition(cond monitor.AlertCondition, + metricDetails *monitor.CommonAlertMetricDetails) { cmp := "" switch cond.Evaluator.Type { case "gt": @@ -468,9 +477,6 @@ func (alert *SCommonAlert) GetCommonAlertMetricDetailsFromAlertCondition(index i metricDetails.Threshold = cond.Evaluator.Params[0] metricDetails.Reduce = cond.Reducer.Type - if fieldOpt != "" { - metricDetails.FieldOpt = strings.Split(fieldOpt, "+")[index] - } q := cond.Query measurement := q.Model.Measurement field := "" @@ -498,14 +504,13 @@ func (alert *SCommonAlert) GetCommonAlertMetricDetailsFromAlertCondition(index i metricDetails.Groupby = groupby //fill measurement\field desciption info - alert.getMetricDescriptionDetails(metricDetails) + getMetricDescriptionDetails(metricDetails) if metricDetails.FieldOpt == "/" { metricDetails.FieldDescription.Unit = "" } - return metricDetails } -func (alert *SCommonAlert) getMetricDescriptionDetails(metricDetails *monitor.CommonAlertMetricDetails) { +func getMetricDescriptionDetails(metricDetails *monitor.CommonAlertMetricDetails) { influxdbMeasurements := DataSourceManager.getMetricDescriptions([]monitor.InfluxMeasurement{monitor. InfluxMeasurement{Measurement: metricDetails.Measurement}}) if len(influxdbMeasurements) == 0 { @@ -610,7 +615,8 @@ func (alert *SCommonAlert) ValidateUpdateData( if err != nil { return data, errors.Wrap(err, "metric_query Unmarshal error") } - err = CommonAlertManager.ValidateMetricQuery(metricQuery) + scope, _ := data.GetString("scope") + err = CommonAlertManager.ValidateMetricQuery(metricQuery, scope, userCred) if err != nil { return data, errors.Wrap(err, "metric query error") } diff --git a/pkg/monitor/models/datasource.go b/pkg/monitor/models/datasource.go index f7c08e0a28..2f530927c1 100644 --- a/pkg/monitor/models/datasource.go +++ b/pkg/monitor/models/datasource.go @@ -266,7 +266,7 @@ func (self *SDataSourceManager) GetMeasurementsWithDescriptionInfos(query jsonut return jsonutils.JSONNull, errors.Wrap(err, "s.GetDefaultSource") } db := influxdb.NewInfluxdb(dataSource.Url) - filterMeasurements, err := self.filterMeasurementsByTime(*db, measurements, query) + filterMeasurements, err := self.filterMeasurementsByTime(*db, measurements, query, tagFilter) if err != nil { return jsonutils.JSONNull, errors.Wrap(err, "filterMeasurementsByTime error") } @@ -390,13 +390,13 @@ type influxdbQueryChan struct { } func (self *SDataSourceManager) filterMeasurementsByTime(db influxdb.SInfluxdb, - measurements []monitor.InfluxMeasurement, query jsonutils.JSONObject) ([]monitor.InfluxMeasurement, + measurements []monitor.InfluxMeasurement, query jsonutils.JSONObject, tagFilter string) ([]monitor.InfluxMeasurement, error) { timeF, err := self.getFromAndToFromParam(query) if err != nil { return nil, err } - filterMeasurements, err := self.getFilterMeasurementsAsyn(timeF.From, timeF.To, measurements, db) + filterMeasurements, err := self.getFilterMeasurementsAsyn(timeF.From, timeF.To, measurements, db, tagFilter) if err != nil { return nil, err } @@ -432,7 +432,7 @@ func (self *SDataSourceManager) getFromAndToFromParam(query jsonutils.JSONObject } func (self *SDataSourceManager) getFilterMeasurementsAsyn(from, to string, - measurements []monitor.InfluxMeasurement, db influxdb.SInfluxdb) ([]monitor.InfluxMeasurement, error) { + measurements []monitor.InfluxMeasurement, db influxdb.SInfluxdb, tagFilter string) ([]monitor.InfluxMeasurement, error) { log.Errorln("start asynchronous task") filterMeasurements := make([]monitor.InfluxMeasurement, 0) queryChan := new(influxdbQueryChan) @@ -445,7 +445,7 @@ func (self *SDataSourceManager) getFilterMeasurementsAsyn(from, to string, for i, _ := range measurements { tmp := measurements[i] measurementQueryGroup.Go(func() error { - return self.getFilterMeasurement(queryChan, from, to, tmp, db) + return self.getFilterMeasurement(queryChan, from, to, tmp, db, tagFilter) }) } measurementQueryGroup.Go(func() error { @@ -466,11 +466,15 @@ func (self *SDataSourceManager) getFilterMeasurementsAsyn(from, to string, } func (self *SDataSourceManager) getFilterMeasurement(queryChan *influxdbQueryChan, from, to string, - measurement monitor.InfluxMeasurement, db influxdb.SInfluxdb) error { + measurement monitor.InfluxMeasurement, db influxdb.SInfluxdb, tagFilter string) error { rtnMeasurement := new(monitor.InfluxMeasurement) var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf(fmt.Sprintf(`SELECT last(*) FROM %s WHERE %s `, measurement.Measurement, - self.renderTimeFilter(from, to)))) + buffer.WriteString(fmt.Sprintf(`SELECT last(*) FROM %s WHERE %s`, measurement.Measurement, + self.renderTimeFilter(from, to))) + if len(tagFilter) != 0 { + buffer.WriteString(" AND ") + buffer.WriteString(fmt.Sprintf(" %s", tagFilter)) + } log.Errorln(buffer.String()) (&db).SetDatabase(measurement.Database) rtn, err := db.Query(buffer.String()) @@ -489,6 +493,9 @@ func (self *SDataSourceManager) getFilterMeasurement(queryChan *influxdbQueryCha } containsVal := false for _, value := range rtn[rtnIndex][serieIndex].Values { + if value[i] == nil { + continue + } floatVal, err := value[i].Float() if err != nil { continue @@ -529,7 +536,7 @@ func (self *SDataSourceManager) renderTimeFilter(from, to string) string { } -func (self *SDataSourceManager) GetMetricMeasurement(query jsonutils.JSONObject) (jsonutils.JSONObject, error) { +func (self *SDataSourceManager) GetMetricMeasurement(query jsonutils.JSONObject, tagFilter string) (jsonutils.JSONObject, error) { database, _ := query.GetString("database") if database == "" { return jsonutils.JSONNull, httperrors.NewInputParameterError("not find database") @@ -582,7 +589,7 @@ func (self *SDataSourceManager) GetMetricMeasurement(query jsonutils.JSONObject) tagValGroup, _ := errgroup.WithContext(ctx) defer cancel() tagValGroup.Go(func() error { - return self.filterTagValue(*output, timeF, db, &tagValChan) + return self.filterTagValue(*output, timeF, db, &tagValChan, tagFilter) }) tagValGroup.Go(func() error { for i := 0; i < tagValChan.count; i++ { @@ -606,7 +613,7 @@ func (self *SDataSourceManager) GetMetricMeasurement(query jsonutils.JSONObject) } func (self *SDataSourceManager) filterTagValue(measurement monitor.InfluxMeasurement, timeF timeFilter, - db *influxdb.SInfluxdb, tagValChan *influxdbTagValueChan) error { + db *influxdb.SInfluxdb, tagValChan *influxdbTagValueChan, tagFilter string) error { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) tagValGroup2, _ := errgroup.WithContext(ctx) tagValChan2 := influxdbTagValueChan{ @@ -617,8 +624,7 @@ func (self *SDataSourceManager) filterTagValue(measurement monitor.InfluxMeasure tmpkey := measurement.TagKey[i] tagValGroup2.Go(func() error { return self.getFilterMeasurementTagValue(&tagValChan2, timeF.From, timeF.To, measurement.FieldKey[0], - tmpkey, - measurement, db) + tmpkey, measurement, db, tagFilter) }) } tagValGroup2.Go(func() error { @@ -812,11 +818,14 @@ type influxdbTagValueChan struct { func (self *SDataSourceManager) getFilterMeasurementTagValue(tagValueChan *influxdbTagValueChan, from string, to string, field string, tagKey string, - measurement monitor.InfluxMeasurement, db *influxdb.SInfluxdb) error { + measurement monitor.InfluxMeasurement, db *influxdb.SInfluxdb, tagFilter string) error { var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf(fmt.Sprintf(`SELECT last("%s") FROM "%s" WHERE %s GROUP BY %q`, - field, measurement.Measurement, - self.renderTimeFilter(from, to), tagKey))) + buffer.WriteString(fmt.Sprintf(`SELECT last("%s") FROM "%s" WHERE %s `, field, measurement.Measurement, + self.renderTimeFilter(from, to))) + if len(tagFilter) != 0 { + buffer.WriteString(fmt.Sprintf(` AND %s `, tagFilter)) + } + buffer.WriteString(fmt.Sprintf(` GROUP BY %q`, tagKey)) rtn, err := db.Query(buffer.String()) log.Errorf("sql:", buffer.String()) if err != nil { diff --git a/pkg/monitor/models/metric.go b/pkg/monitor/models/metric.go index d35ba9a691..f6e54e5cf1 100644 --- a/pkg/monitor/models/metric.go +++ b/pkg/monitor/models/metric.go @@ -576,3 +576,37 @@ func getUnInsertFields(searchFields []monitor.MetricFieldCreateInput, } return unInsertFields, updateFields } + +func (self *SMetricMeasurement) getMetricJoint() ([]SMetric, error) { + metricJoint := make([]SMetric, 0) + q := MetricManager.Query().Equals(MetricManager.GetMasterFieldName(), self.Id) + if err := db.FetchModelObjects(MetricManager, q, &metricJoint); err != nil { + return nil, err + } + return metricJoint, nil +} + +func (self *SMetricMeasurement) CustomizeDelete( + ctx context.Context, userCred mcclient.TokenCredential, + query jsonutils.JSONObject, data jsonutils.JSONObject) error { + metricJoint, err := self.getMetricJoint() + if err != nil { + return err + } + for _, joint := range metricJoint { + field, err := joint.GetMetricField() + if err != nil { + return err + } + if err := field.CustomizeDelete(ctx, userCred, query, data); err != nil { + return err + } + if err := field.Delete(ctx, userCred); err != nil { + return err + } + if err := joint.Detach(ctx, userCred); err != nil { + return err + } + } + return nil +} diff --git a/pkg/monitor/models/metric_field.go b/pkg/monitor/models/metric_field.go index 7e34b2162c..b95eb7b10d 100644 --- a/pkg/monitor/models/metric_field.go +++ b/pkg/monitor/models/metric_field.go @@ -2,6 +2,7 @@ package models import ( "context" + "database/sql" "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" @@ -118,6 +119,18 @@ func (manager *SMetricFieldManager) ListItemFilter( return q, nil } +func (man *SMetricFieldManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []monitor.MetricFieldDetail { + rows := make([]monitor.MetricFieldDetail, len(objs)) + return rows +} + func (man *SMetricFieldManager) OrderByExtraFields( ctx context.Context, q *sqlchemy.SQuery, @@ -145,3 +158,38 @@ func (manager *SMetricFieldManager) SaveMetricField(ctx context.Context, userCre } return obj.(*SMetricField), nil } + +func (man *SMetricFieldManager) GetField(id string) (*SMetricField, error) { + obj, err := man.FetchById(id) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, nil + } + return nil, err + } + return obj.(*SMetricField), nil +} + +func (self *SMetricField) CustomizeDelete( + ctx context.Context, userCred mcclient.TokenCredential, + query jsonutils.JSONObject, data jsonutils.JSONObject) error { + metricJoint, err := self.getMetricJoint() + if err != nil { + return err + } + for _, joint := range metricJoint { + if err := joint.Detach(ctx, userCred); err != nil { + return err + } + } + return nil +} + +func (self *SMetricField) getMetricJoint() ([]SMetric, error) { + metricJoint := make([]SMetric, 0) + q := MetricManager.Query().Equals(MetricManager.GetSlaveFieldName(), self.Id) + if err := db.FetchModelObjects(MetricManager, q, &metricJoint); err != nil { + return nil, err + } + return metricJoint, nil +} diff --git a/pkg/monitor/models/metric_joint.go b/pkg/monitor/models/metric_joint.go index dac92b7fc8..bf3459833c 100644 --- a/pkg/monitor/models/metric_joint.go +++ b/pkg/monitor/models/metric_joint.go @@ -4,6 +4,7 @@ import ( "context" "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/mcclient" ) type SMetricManager struct { @@ -48,3 +49,11 @@ func (metric *SMetric) DoSave(ctx context.Context) error { metric.SetModelManager(MetricManager, metric) return nil } + +func (self *SMetric) GetMetricField() (*SMetricField, error) { + return MetricFieldManager.GetField(self.FieldId) +} + +func (joint *SMetric) Detach(ctx context.Context, userCred mcclient.TokenCredential) error { + return db.DetachJoint(ctx, userCred, joint) +} diff --git a/pkg/monitor/models/suggestsysrule.go b/pkg/monitor/models/suggestsysrule.go index 10a259b478..63b0e1b98d 100644 --- a/pkg/monitor/models/suggestsysrule.go +++ b/pkg/monitor/models/suggestsysrule.go @@ -31,6 +31,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/influxdb" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -364,7 +365,33 @@ func (self *SSuggestSysRuleManager) AllowGetPropertyMetricMeasurement(ctx contex func (self *SSuggestSysRuleManager) GetPropertyMetricMeasurement(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { - return DataSourceManager.GetMetricMeasurement(query) + return self.GetMetricMeasurement(query) +} + +func (self *SSuggestSysRuleManager) GetMetricMeasurement(query jsonutils.JSONObject) (jsonutils.JSONObject, error) { + database, _ := query.GetString("database") + if database == "" { + return jsonutils.JSONNull, httperrors.NewInputParameterError("not support database") + } + measurement, _ := query.GetString("measurement") + if measurement == "" { + return jsonutils.JSONNull, httperrors.NewInputParameterError("not support measurement") + } + dataSource, err := DataSourceManager.GetDefaultSource() + if err != nil { + return jsonutils.JSONNull, errors.Wrap(err, "s.GetDefaultSource") + } + + db := influxdb.NewInfluxdb(dataSource.Url) + db.SetDatabase(database) + output := new(monitor.InfluxMeasurement) + output.Measurement = measurement + output.Database = database + err = getAttributesOnMeasurement(database, monitor.METRIC_FIELD, output, db) + if err != nil { + return jsonutils.JSONNull, errors.Wrap(err, "getAttributesOnMeasurement error") + } + return jsonutils.Marshal(output), nil } func (man *SSuggestSysRuleManager) GetRuleByType(tp monitor.SuggestDriverType) (*SSuggestSysRule, error) { diff --git a/pkg/monitor/models/unifiedmonitor.go b/pkg/monitor/models/unifiedmonitor.go index 2c46952f5a..102dcd082d 100644 --- a/pkg/monitor/models/unifiedmonitor.go +++ b/pkg/monitor/models/unifiedmonitor.go @@ -1,7 +1,6 @@ package models import ( - "bytes" "context" "fmt" "strings" @@ -15,10 +14,10 @@ import ( "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" - "yunion.io/x/onecloud/pkg/mcclient/modules" mq "yunion.io/x/onecloud/pkg/monitor/metricquery" "yunion.io/x/onecloud/pkg/monitor/tsdb" "yunion.io/x/onecloud/pkg/monitor/validators" + "yunion.io/x/onecloud/pkg/util/rbacutils" ) var ( @@ -60,20 +59,22 @@ func (self *SUnifiedMonitorManager) AllowGetPropertyMeasurements(ctx context.Con func (self *SUnifiedMonitorManager) GetPropertyMeasurements(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { - var filter string - var err error - if scope, err := query.GetString("scope"); err == nil { - filter, err = filterByScope(ctx, scope, query) - } else { - filter, err = filterByCredential(userCred) - } + filter, err := getTagFilterByRequestQuery(ctx, query) if err != nil { return nil, err } return DataSourceManager.GetMeasurementsWithDescriptionInfos(query, "", filter) } +func getTagFilterByRequestQuery(ctx context.Context, query jsonutils.JSONObject) (filter string, err error) { + + if scope, err := query.GetString("scope"); err == nil { + filter, err = filterByScope(ctx, scope, query) + } + return +} + func filterByScope(ctx context.Context, scope string, data jsonutils.JSONObject) (string, error) { domainId := jsonutils.GetAnyString(data, []string{"domain_id", "domain", "project_domain_id", "project_domain"}) projectId := jsonutils.GetAnyString(data, []string{"project_id", "project"}) @@ -111,21 +112,6 @@ func filterByScope(ctx context.Context, scope string, data jsonutils.JSONObject) return "", fmt.Errorf("scope is illegal") } -func filterByCredential(userCred mcclient.TokenCredential) (string, error) { - roles := userCred.GetRoles() - roleStr := strings.Join(roles, ",") - if strings.Contains(roleStr, "admin") { - return getTenantIdStr("admin", userCred) - } - if strings.Contains(roleStr, "domainadmin") { - return getTenantIdStr("admin", userCred) - } - if strings.Contains(roleStr, "member") { - return getTenantIdStr("admin", userCred) - } - return "", errors.Wrap(errors.ErrNotFound, "user role") -} - func getTenantIdStr(role string, userCred mcclient.TokenCredential) (string, error) { if role == "admin" { return "", nil @@ -142,26 +128,30 @@ func getTenantIdStr(role string, userCred mcclient.TokenCredential) (string, err } func getProjectIdsFilterByDomain(domainId string) (string, error) { - s := auth.GetAdminSession(context.Background(), "", "") - params := jsonutils.Marshal(map[string]string{"domain_id": domainId}) - tenants, err := modules.Projects.List(s, params) - if err != nil { - return "", errors.Wrap(err, "Projects.List") - } - var buffer bytes.Buffer - for index, tenant := range tenants.Data { - tenantId, _ := tenant.GetString("id") - if index != len(tenants.Data)-1 { - buffer.WriteString(fmt.Sprintf(" %s =~ /%s/ %s ", "tenant_id", tenantId, "OR")) - } else { - buffer.WriteString(fmt.Sprintf(" %s =~ /%s/ ", "tenant_id", tenantId)) - } - } - return buffer.String(), nil + //s := auth.GetAdminSession(context.Background(), "", "") + //params := jsonutils.Marshal(map[string]string{"domain_id": domainId}) + //tenants, err := modules.Projects.List(s, params) + //if err != nil { + // return "", errors.Wrap(err, "Projects.List") + //} + //var buffer bytes.Buffer + //buffer.WriteString("( ") + //for index, tenant := range tenants.Data { + // tenantId, _ := tenant.GetString("id") + // if index != len(tenants.Data)-1 { + // buffer.WriteString(fmt.Sprintf(" %s =~ /%s/ %s ", "tenant_id", tenantId, "OR")) + // } else { + // buffer.WriteString(fmt.Sprintf(" %s =~ /%s/ ", "tenant_id", tenantId)) + // } + //} + //buffer.WriteString(" )") + //return buffer.String(), nil + return fmt.Sprintf(`"%s" =~ /%s/`, "domain_id", domainId), nil + } func getProjectIdFilterByProject(projectId string) (string, error) { - return fmt.Sprintf("%s =~ /%s/", "tenant_id", projectId), nil + return fmt.Sprintf(`"%s" =~ /%s/`, "tenant_id", projectId), nil } func (self *SUnifiedMonitorManager) AllowGetPropertyMetricMeasurement(ctx context.Context, @@ -178,7 +168,11 @@ func (self *SUnifiedMonitorManager) GetPropertyMetricMeasurement(ctx context.Con GroupOptType: monitor.UNIFIED_MONITOR_GROUPBY_OPT_TYPE, GroupOptValue: monitor.UNIFIED_MONITOR_GROUPBY_OPT_VALUE, } - rtn, err := DataSourceManager.GetMetricMeasurement(query) + filter, err := getTagFilterByRequestQuery(ctx, query) + if err != nil { + return nil, err + } + rtn, err := DataSourceManager.GetMetricMeasurement(query, filter) if err != nil { return nil, err } @@ -201,7 +195,12 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m return nil, httperrors.NewInputParameterError("no metric_query field in param") } for _, q := range inputQuery.MetricQuery { - setDefaultValue(q, inputQuery) + scope, _ := data.GetString("scope") + ownId, _ := self.FetchOwnerId(ctx, data) + if ownId == nil { + ownId = userCred + } + setDefaultValue(q, inputQuery, scope, ownId) err = self.ValidateInputQuery(q) if err != nil { return jsonutils.NewDict(), err @@ -260,7 +259,8 @@ func (self *SUnifiedMonitorManager) ValidateInputQuery(query *monitor.AlertQuery return validators.ValidateSelectOfMetricQuery(*query) } -func setDefaultValue(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQuery) { +func setDefaultValue(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQuery, + scope string, ownerId mcclient.IIdentityProvider) { setDataSourceId(query) query.From = inputQuery.From query.To = inputQuery.To @@ -294,6 +294,25 @@ func setDefaultValue(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQ }) query.Model.Selects[i] = sel } + var projectId, domainId string + switch rbacutils.TRbacScope(scope) { + case rbacutils.ScopeProject: + projectId = ownerId.GetProjectId() + query.Model.Tags = append(query.Model.Tags, monitor.MetricQueryTag{ + Key: "tenant_id", + Operator: "=", + Value: projectId, + Condition: "and", + }) + case rbacutils.ScopeDomain: + domainId = ownerId.GetProjectDomainId() + query.Model.Tags = append(query.Model.Tags, monitor.MetricQueryTag{ + Key: "domain_id", + Operator: "=", + Value: domainId, + Condition: "and", + }) + } } func setDataSourceId(query *monitor.AlertQuery) { diff --git a/pkg/monitor/service/handlers.go b/pkg/monitor/service/handlers.go index 4149b621ab..9748a1eb86 100644 --- a/pkg/monitor/service/handlers.go +++ b/pkg/monitor/service/handlers.go @@ -61,6 +61,7 @@ func InitHandlers(app *appsrv.Application) { models.MetricMeasurementManager, models.MetricFieldManager, models.AlertRecordManager, + models.AlertDashBoardManager, } { db.RegisterModelManager(manager) handler := db.NewModelHandler(manager)