mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-09 23:28:20 +08:00
584 lines
18 KiB
Go
584 lines
18 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package models
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/gotypes"
|
|
"yunion.io/x/pkg/util/rbacscope"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis/monitor"
|
|
"yunion.io/x/onecloud/pkg/appsrv"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/monitor/datasource"
|
|
merrors "yunion.io/x/onecloud/pkg/monitor/errors"
|
|
mq "yunion.io/x/onecloud/pkg/monitor/metricquery"
|
|
"yunion.io/x/onecloud/pkg/monitor/options"
|
|
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
|
"yunion.io/x/onecloud/pkg/monitor/validators"
|
|
"yunion.io/x/onecloud/pkg/util/influxdb"
|
|
)
|
|
|
|
const (
|
|
TELEGRAF_DATABASE = "telegraf"
|
|
)
|
|
|
|
var (
|
|
UnifiedMonitorManager *SUnifiedMonitorManager
|
|
)
|
|
|
|
func init() {
|
|
UnifiedMonitorManager = &SUnifiedMonitorManager{
|
|
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
|
&SUnifiedMonitorManager{},
|
|
"",
|
|
"unifiedmonitor",
|
|
"unifiedmonitors",
|
|
),
|
|
}
|
|
UnifiedMonitorManager.SetVirtualObject(UnifiedMonitorManager)
|
|
}
|
|
|
|
type SUnifiedMonitorManager struct {
|
|
db.SVirtualResourceBaseManager
|
|
}
|
|
|
|
type SUnifiedMonitorModel struct {
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) GetPropertyDatabases(ctx context.Context, userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
return DataSourceManager.GetDatabases()
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) GetPropertyMeasurements(ctx context.Context, userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
|
|
filter, err := getTagFilterByRequestQuery(ctx, userCred, query)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "getTagFilterByRequestQuery")
|
|
}
|
|
return DataSourceManager.GetMeasurementsWithDescriptionInfos(query, "", filter)
|
|
}
|
|
|
|
func getTagFilterByRequestQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*monitor.MetricQueryTag, error) {
|
|
|
|
scope, _ := query.GetString("scope")
|
|
return filterByScope(ctx, userCred, scope, query)
|
|
}
|
|
|
|
func filterByScope(ctx context.Context, userCred mcclient.TokenCredential, scope string, data jsonutils.JSONObject) (*monitor.MetricQueryTag, error) {
|
|
domainId := jsonutils.GetAnyString(data, db.DomainFetchKeys)
|
|
projectId := jsonutils.GetAnyString(data, db.ProjectFetchKeys)
|
|
if projectId != "" {
|
|
project, err := db.DefaultProjectFetcher(ctx, projectId, domainId)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "db.DefaultProjectFetcher")
|
|
}
|
|
projectId = project.GetProjectId()
|
|
domainId = project.GetProjectDomainId()
|
|
}
|
|
if domainId != "" {
|
|
domain, err := db.DefaultDomainFetcher(ctx, domainId)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "db.DefaultDomainFetcher")
|
|
}
|
|
domainId = domain.GetProjectDomainId()
|
|
domain.GetProjectId()
|
|
}
|
|
switch scope {
|
|
case "system":
|
|
return nil, nil
|
|
case "domain":
|
|
if domainId == "" {
|
|
domainId = userCred.GetProjectDomainId()
|
|
}
|
|
return getProjectIdsFilterByDomain(domainId)
|
|
default:
|
|
if projectId == "" {
|
|
projectId = userCred.GetProjectId()
|
|
}
|
|
return getProjectIdFilterByProject(projectId)
|
|
}
|
|
}
|
|
|
|
func getTenantIdStr(role string, userCred mcclient.TokenCredential) (*monitor.MetricQueryTag, error) {
|
|
if role == "admin" {
|
|
return nil, nil
|
|
}
|
|
if role == "domainadmin" {
|
|
domainId := userCred.GetDomainId()
|
|
return getProjectIdsFilterByDomain(domainId)
|
|
}
|
|
if role == "member" {
|
|
tenantId := userCred.GetProjectId()
|
|
return getProjectIdFilterByProject(tenantId)
|
|
}
|
|
return nil, errors.Wrapf(errors.ErrNotFound, "not supported role %q", role)
|
|
}
|
|
|
|
func getProjectIdsFilterByDomain(domainId string) (*monitor.MetricQueryTag, 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
|
|
//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 &monitor.MetricQueryTag{
|
|
Key: "domain_id",
|
|
Operator: "=~",
|
|
Value: fmt.Sprintf("/%s/", domainId),
|
|
}, nil
|
|
//return fmt.Sprintf(`%s =~ /%s/`, "domain_id", domainId), nil
|
|
}
|
|
|
|
func getProjectIdFilterByProject(projectId string) (*monitor.MetricQueryTag, error) {
|
|
//return fmt.Sprintf(`%s =~ /%s/`, "tenant_id", projectId), nil
|
|
return &monitor.MetricQueryTag{
|
|
Key: "tenant_id",
|
|
Operator: "=~",
|
|
Value: fmt.Sprintf("/%s/", projectId),
|
|
}, nil
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) GetPropertyMetricMeasurement(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
metricFunc := monitor.MetricFunc{
|
|
FieldOptType: monitor.UNIFIED_MONITOR_FIELD_OPT_TYPE,
|
|
FieldOptValue: monitor.UNIFIED_MONITOR_FIELD_OPT_VALUE,
|
|
GroupOptType: monitor.UNIFIED_MONITOR_GROUPBY_OPT_TYPE,
|
|
GroupOptValue: monitor.UNIFIED_MONITOR_GROUPBY_OPT_VALUE,
|
|
}
|
|
filter, err := getTagFilterByRequestQuery(ctx, userCred, query)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "getTagFilterByRequestQuery %s", query.String())
|
|
}
|
|
rtn, err := DataSourceManager.GetMetricMeasurement(userCred, query, filter)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "GetMetricMeasurement by query %s, filter %s", query.String(), filter)
|
|
}
|
|
rtn.(*jsonutils.JSONDict).Add(jsonutils.Marshal(&metricFunc), "func")
|
|
return rtn, nil
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration {
|
|
return 5 * time.Minute
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
tmp := jsonutils.DeepCopy(data)
|
|
self.handleDataPreSignature(ctx, tmp)
|
|
if !options.Options.DisableQuerySignatureCheck {
|
|
if err := ValidateQuerySignature(tmp); err != nil {
|
|
return nil, errors.Wrap(err, "ValidateQuerySignature")
|
|
}
|
|
}
|
|
inputQuery := new(monitor.MetricInputQuery)
|
|
err := data.Unmarshal(inputQuery)
|
|
if err != nil {
|
|
return jsonutils.NewDict(), err
|
|
}
|
|
if len(inputQuery.MetricQuery) == 0 {
|
|
return nil, merrors.NewArgIsEmptyErr("metric_query")
|
|
}
|
|
for _, q := range inputQuery.MetricQuery {
|
|
scope, _ := data.GetString("scope")
|
|
ownId, _ := self.FetchOwnerId(ctx, data)
|
|
if ownId == nil {
|
|
ownId = userCred
|
|
}
|
|
setDefaultValue(q, inputQuery, scope, ownId)
|
|
if err := self.ValidateInputQuery(q); err != nil {
|
|
return jsonutils.NewDict(), errors.Wrapf(err, "ValidateInputQuery")
|
|
}
|
|
}
|
|
|
|
var groupByTag = make([]string, 0)
|
|
for _, query := range inputQuery.MetricQuery {
|
|
for _, group := range query.Model.GroupBy {
|
|
if group.Type == "tag" {
|
|
groupByTag = append(groupByTag, group.Params[0])
|
|
}
|
|
}
|
|
}
|
|
|
|
rtn, err := doQuery(userCred, *inputQuery)
|
|
if err != nil {
|
|
return jsonutils.NewDict(), errors.Wrapf(err, "doQuery with input %s", data)
|
|
}
|
|
|
|
if len(inputQuery.Soffset) != 0 && len(inputQuery.Slimit) != 0 {
|
|
// seriesTotal := self.fillSearchSeriesTotalQuery(userCred, *inputQuery.MetricQuery[0])
|
|
// do offset and limit
|
|
total := rtn.SeriesTotal
|
|
offset, err := strconv.Atoi(inputQuery.Soffset)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError("soffset %q is not integer", inputQuery.Soffset)
|
|
}
|
|
limit, err := strconv.Atoi(inputQuery.Slimit)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError("slimit %q is not integer", inputQuery.Slimit)
|
|
}
|
|
start := offset
|
|
end := start + limit
|
|
if end > int(total) {
|
|
end = int(total)
|
|
}
|
|
ss := rtn.Series
|
|
if start >= end {
|
|
rtn.Series = nil
|
|
} else {
|
|
rtn.Series = ss[start:end]
|
|
}
|
|
}
|
|
|
|
fillSerieTags(&rtn.Series)
|
|
return jsonutils.Marshal(rtn), nil
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) fillSearchSeriesTotalQuery(userCred mcclient.TokenCredential, fork monitor.AlertQuery) int64 {
|
|
newGroupByPart := make([]monitor.MetricQueryPart, 0)
|
|
newGroupByPart = append(newGroupByPart, fork.Model.GroupBy[0])
|
|
fork.Model.GroupBy = newGroupByPart
|
|
forkInputQury := new(monitor.MetricInputQuery)
|
|
forkInputQury.MetricQuery = []*monitor.AlertQuery{&fork}
|
|
rtn, err := doQuery(userCred, *forkInputQury)
|
|
if err != nil {
|
|
log.Errorf("exec forkInputQury err:%v", err)
|
|
return 0
|
|
}
|
|
return int64(len(rtn.Series))
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) handleDataPreSignature(ctx context.Context, data jsonutils.JSONObject) {
|
|
scope, _ := data.GetString("scope")
|
|
isIdentityName, _ := data.Bool("identity_name")
|
|
switch scope {
|
|
case "system":
|
|
case "domain":
|
|
domain, err := data.GetString("project_domain")
|
|
if err == nil {
|
|
domainObj, _ := db.DefaultDomainFetcher(ctx, domain)
|
|
if isIdentityName {
|
|
domain = domainObj.Name
|
|
}
|
|
data.(*jsonutils.JSONDict).Remove("project_domain")
|
|
data.(*jsonutils.JSONDict).Set("domain_id", jsonutils.NewString(domain))
|
|
}
|
|
default:
|
|
project, err := data.GetString("project")
|
|
if err == nil {
|
|
domain, _ := data.GetString("project_domain")
|
|
tenant, _ := db.DefaultProjectFetcher(ctx, project, domain)
|
|
if isIdentityName {
|
|
project = tenant.Name
|
|
}
|
|
data.(*jsonutils.JSONDict).Remove("project")
|
|
data.(*jsonutils.JSONDict).Set("project_id", jsonutils.NewString(project))
|
|
}
|
|
}
|
|
}
|
|
|
|
func doQuery(userCred mcclient.TokenCredential, query monitor.MetricInputQuery) (*mq.Metrics, error) {
|
|
conditions := make([]*monitor.AlertCondition, 0)
|
|
for _, q := range query.MetricQuery {
|
|
condition := monitor.AlertCondition{
|
|
Type: "metricquery",
|
|
Query: *q,
|
|
}
|
|
conditions = append(conditions, &condition)
|
|
}
|
|
factory := mq.GetQueryFactories()["metricquery"]
|
|
metricQ, err := factory(conditions)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "factory")
|
|
}
|
|
metrics, err := metricQ.ExecuteQuery(userCred, query.SkipCheckSeries)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "ExecuteQuery")
|
|
}
|
|
// drop metas contains raw_query
|
|
if !query.ShowMeta {
|
|
metrics.Metas = nil
|
|
}
|
|
metrics.SeriesTotal = int64(len(metrics.Series))
|
|
return metrics, nil
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) ValidateInputQuery(query *monitor.AlertQuery) error {
|
|
if query.From == "" {
|
|
query.From = "1h"
|
|
}
|
|
if query.Model.Interval == "" {
|
|
query.Model.Interval = "5m"
|
|
}
|
|
if query.To == "" {
|
|
query.To = "now"
|
|
}
|
|
if _, err := time.ParseDuration(query.Model.Interval); err != nil {
|
|
return httperrors.NewInputParameterError("Invalid interval format: %s", query.Model.Interval)
|
|
}
|
|
return validators.ValidateSelectOfMetricQuery(*query)
|
|
}
|
|
|
|
func setDefaultValue(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQuery,
|
|
scope string, ownerId mcclient.IIdentityProvider) {
|
|
query.From = inputQuery.From
|
|
query.To = inputQuery.To
|
|
query.Model.Interval = inputQuery.Interval
|
|
|
|
metricMeasurement, _ := MetricMeasurementManager.GetCache().Get(query.Model.Measurement)
|
|
|
|
checkQueryGroupBy(query, inputQuery)
|
|
|
|
if len(inputQuery.Interval) != 0 {
|
|
query.Model.GroupBy = append(query.Model.GroupBy,
|
|
monitor.MetricQueryPart{
|
|
Type: "time",
|
|
Params: []string{"$interval"},
|
|
},
|
|
monitor.MetricQueryPart{
|
|
Type: "fill",
|
|
Params: []string{"none"},
|
|
})
|
|
}
|
|
|
|
// HACK: not set slimit and soffset, getting all series then do offset and limit
|
|
// if len(inputQuery.Slimit) != 0 && len(inputQuery.Soffset) != 0 {
|
|
// query.Model.GroupBy = append(query.Model.GroupBy,
|
|
// monitor.MetricQueryPart{Type: "slimit", Params: []string{inputQuery.Slimit}},
|
|
// monitor.MetricQueryPart{Type: "soffset", Params: []string{inputQuery.Soffset}},
|
|
// )
|
|
// }
|
|
|
|
if query.Model.Database == "" {
|
|
database := ""
|
|
if metricMeasurement == nil {
|
|
log.Warningf("Not found measurement %s from metrics measurement cache", query.Model.Measurement)
|
|
} else {
|
|
database = metricMeasurement.Database
|
|
}
|
|
if database == "" {
|
|
// hack: query from default telegraf database if no metric measurement matched
|
|
database = TELEGRAF_DATABASE
|
|
}
|
|
query.Model.Database = database
|
|
}
|
|
|
|
for i, sel := range query.Model.Selects {
|
|
if len(sel) > 1 {
|
|
continue
|
|
}
|
|
sel = append(sel, monitor.MetricQueryPart{
|
|
Type: "mean",
|
|
Params: []string{},
|
|
})
|
|
query.Model.Selects[i] = sel
|
|
}
|
|
var projectId, domainId string
|
|
switch rbacscope.TRbacScope(scope) {
|
|
case rbacscope.ScopeProject:
|
|
projectId = ownerId.GetProjectId()
|
|
containId := false
|
|
for _, tagFilter := range query.Model.Tags {
|
|
if tagFilter.Key == "tenant_id" {
|
|
containId = true
|
|
break
|
|
}
|
|
}
|
|
if !containId {
|
|
query.Model.Tags = append(query.Model.Tags, monitor.MetricQueryTag{
|
|
Key: "tenant_id",
|
|
Operator: "=",
|
|
Value: projectId,
|
|
Condition: "and",
|
|
})
|
|
}
|
|
case rbacscope.ScopeDomain:
|
|
domainId = ownerId.GetProjectDomainId()
|
|
containId := false
|
|
for _, tagFilter := range query.Model.Tags {
|
|
if tagFilter.Key == "domain_id" {
|
|
containId = true
|
|
break
|
|
}
|
|
}
|
|
if !containId {
|
|
query.Model.Tags = append(query.Model.Tags, monitor.MetricQueryTag{
|
|
Key: "domain_id",
|
|
Operator: "=",
|
|
Value: domainId,
|
|
Condition: "and",
|
|
})
|
|
}
|
|
}
|
|
if metricMeasurement != nil && metricMeasurement.ResType == hostconsts.TELEGRAF_TAG_ONECLOUD_RES_TYPE {
|
|
query.Model.Tags = append(query.Model.Tags, monitor.MetricQueryTag{
|
|
Key: hostconsts.TELEGRAF_TAG_KEY_RES_TYPE,
|
|
Operator: "=",
|
|
Value: hostconsts.TELEGRAF_TAG_ONECLOUD_RES_TYPE,
|
|
Condition: "and",
|
|
})
|
|
}
|
|
}
|
|
|
|
func checkQueryGroupBy(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQuery) {
|
|
if len(query.Model.GroupBy) != 0 {
|
|
return
|
|
}
|
|
metricMeasurement, _ := MetricMeasurementManager.GetCache().Get(query.Model.Measurement)
|
|
if inputQuery.Unit || metricMeasurement == nil && query.Model.Database == monitor.METRIC_DATABASE_METER {
|
|
return
|
|
}
|
|
tagId := ""
|
|
if metricMeasurement != nil {
|
|
tagId = monitor.MEASUREMENT_TAG_ID[metricMeasurement.ResType]
|
|
}
|
|
if len(tagId) == 0 || (len(inputQuery.Slimit) != 0 && len(inputQuery.Soffset) != 0) {
|
|
tagId = "*"
|
|
}
|
|
query.Model.GroupBy = append(query.Model.GroupBy,
|
|
monitor.MetricQueryPart{
|
|
Type: "field",
|
|
Params: []string{tagId},
|
|
})
|
|
}
|
|
|
|
func fillSerieTags(series *tsdb.TimeSeriesSlice) {
|
|
for i, serie := range *series {
|
|
for _, tag := range []string{"brand", "platform", "hypervisor"} {
|
|
if val, ok := serie.Tags[tag]; ok {
|
|
serie.Tags["brand"] = val
|
|
break
|
|
}
|
|
}
|
|
for _, tag := range []string{"source", "status", hostconsts.TELEGRAF_TAG_KEY_HOST_TYPE,
|
|
hostconsts.TELEGRAF_TAG_KEY_RES_TYPE, "cpu", "is_vm", "os_type", "domain_name", "region"} {
|
|
if _, ok := serie.Tags[tag]; ok {
|
|
delete(serie.Tags, tag)
|
|
}
|
|
}
|
|
(*series)[i] = serie
|
|
}
|
|
}
|
|
|
|
func (self *SUnifiedMonitorManager) GetPropertySimpleQuery(ctx context.Context, userCred mcclient.TokenCredential,
|
|
input *monitor.SimpleQueryInput) (jsonutils.JSONObject, error) {
|
|
if len(input.Database) == 0 {
|
|
input.Database = "telegraf"
|
|
}
|
|
if len(input.MetricName) == 0 {
|
|
return nil, httperrors.NewMissingParameterError("metric_name")
|
|
}
|
|
metric := strings.Split(input.MetricName, ".")
|
|
if len(metric) != 2 {
|
|
return nil, httperrors.NewInputParameterError("invalid metric_name %s", input.MetricName)
|
|
}
|
|
measurement, field := metric[0], metric[1]
|
|
sqlstr := "select id, " + field + " from " + measurement + " where "
|
|
if input.Tags == nil {
|
|
input.Tags = map[string]string{}
|
|
}
|
|
if len(input.Id) > 0 {
|
|
input.Tags["id"] = input.Id
|
|
}
|
|
if input.EndTime.IsZero() {
|
|
input.EndTime = time.Now()
|
|
}
|
|
if input.StartTime.IsZero() {
|
|
input.StartTime = input.EndTime.Add(time.Hour * -1)
|
|
}
|
|
if input.EndTime.Sub(input.StartTime).Hours() > 1 {
|
|
return nil, httperrors.NewInputParameterError("The query interval is greater than one hour")
|
|
}
|
|
st := input.StartTime.Format(time.RFC3339)
|
|
et := input.EndTime.Format(time.RFC3339)
|
|
conditions := []string{}
|
|
for k, v := range input.Tags {
|
|
conditions = append(conditions, fmt.Sprintf("%s = '%s'", k, v))
|
|
}
|
|
conditions = append(conditions, fmt.Sprintf("time >= '%s'", st))
|
|
conditions = append(conditions, fmt.Sprintf("time <= '%s'", et))
|
|
sqlstr += strings.Join(conditions, " and ")
|
|
sqlstr += " limit 2000"
|
|
dataSource, err := datasource.GetDefaultSource(input.Database)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "s.GetDefaultSource")
|
|
}
|
|
db := influxdb.NewInfluxdb(dataSource.Url)
|
|
err = db.SetDatabase(input.Database)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError("not support database %s", input.Database)
|
|
}
|
|
dbRtn, err := db.Query(sqlstr)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "sql selected error")
|
|
}
|
|
ret := []monitor.SimpleQueryOutput{}
|
|
for _, dbResults := range dbRtn {
|
|
for _, dbResult := range dbResults {
|
|
for _, value := range dbResult.Values {
|
|
if len(value) != 3 ||
|
|
gotypes.IsNil(value[0]) ||
|
|
gotypes.IsNil(value[1]) ||
|
|
gotypes.IsNil(value[2]) {
|
|
continue
|
|
}
|
|
timestamp, err := value[0].Int()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
id, err := value[1].GetString()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
v, err := value[2].Float()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ret = append(ret, monitor.SimpleQueryOutput{
|
|
Id: id,
|
|
Time: time.UnixMilli(timestamp),
|
|
Value: v,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return jsonutils.Marshal(map[string]interface{}{"values": ret}), nil
|
|
}
|