mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
fix(monitor): make simple-query API adapt to victoriametrics
This commit is contained in:
+60
-51
@@ -14,50 +14,12 @@
|
||||
|
||||
package monitor
|
||||
|
||||
type DataSourceConfig struct {
|
||||
Id string
|
||||
Name string
|
||||
Driver string
|
||||
Config interface{}
|
||||
}
|
||||
import "fmt"
|
||||
|
||||
type MetricResource struct {
|
||||
// Type is the metric resource type. e.g: host, vm, lbinstance
|
||||
Type string `json:"type"`
|
||||
// ConfigId is the data source config id
|
||||
ConfigId string `json:"config_id"`
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
Resource MetricResource `json:"resource"`
|
||||
Measurement string `json:"measurement"`
|
||||
Field string `json:"field"`
|
||||
DisplayName string `json:"displayname"`
|
||||
}
|
||||
|
||||
type TimeSeries struct {
|
||||
Results []TimeSeriesResult `json:"results"`
|
||||
}
|
||||
|
||||
type TimeSeriesResult struct {
|
||||
Series []TimeSeriesRow `json:"series"`
|
||||
}
|
||||
|
||||
type TimeSeriesRow struct {
|
||||
Metric Metric `json:"metric"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Columns []string `json:"columns,omitempty"`
|
||||
// Value item is a point with timestamp and value
|
||||
Values [][]interface{} `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
type MetricRequest struct {
|
||||
// The start time for the query
|
||||
From string `json:"from"`
|
||||
// An end time for the query
|
||||
To string `json:"to"`
|
||||
Queries []*MetricQuery `json:"queries"`
|
||||
Debug bool `json:"debug"`
|
||||
type Condition struct {
|
||||
Type string `json:"type"`
|
||||
Params []float64 `json:"params"`
|
||||
Operators []string `json:"operators"`
|
||||
}
|
||||
|
||||
type MetricQueryTag struct {
|
||||
@@ -72,6 +34,61 @@ type MetricQueryPart struct {
|
||||
Params []string `json:"params"`
|
||||
}
|
||||
|
||||
func NewMetricQueryPartField(fieldName string) MetricQueryPart {
|
||||
return MetricQueryPart{
|
||||
Type: "field",
|
||||
Params: []string{fieldName},
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricQueryPartAS(alias string) MetricQueryPart {
|
||||
return MetricQueryPart{
|
||||
Type: "alias",
|
||||
Params: []string{alias},
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricQueryPartMath(op string, val string) MetricQueryPart {
|
||||
return MetricQueryPart{
|
||||
Type: "math",
|
||||
Params: []string{fmt.Sprintf("%s %s", op, val)},
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricQueryPartFunc(funcName string) MetricQueryPart {
|
||||
return MetricQueryPart{
|
||||
Type: funcName,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricQueryPartMean() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("mean")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartCount() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("count")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartDistinct() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("distinct")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartSum() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("sum")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartMin() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("min")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartMax() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("max")
|
||||
}
|
||||
|
||||
func NewMetricQueryPartLast() MetricQueryPart {
|
||||
return NewMetricQueryPartFunc("last")
|
||||
}
|
||||
|
||||
type MetricQuerySelect []MetricQueryPart
|
||||
|
||||
func NewMetricQuerySelect(parts ...MetricQueryPart) MetricQuerySelect {
|
||||
@@ -90,11 +107,3 @@ type MetricQuery struct {
|
||||
Policy string `json:"policy"`
|
||||
ResultFormat string `json:"result_format"`
|
||||
}
|
||||
|
||||
type AlertConditionCombiner string
|
||||
|
||||
type Condition struct {
|
||||
Type string `json:"type"`
|
||||
Params []float64 `json:"params"`
|
||||
Operators []string `json:"operators"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
func TestMetricQueryInput_AddMetric(t *testing.T) {
|
||||
q := NewMetricQuery("cpu")
|
||||
q.Select("usage_active").MEAN()
|
||||
q.Select("usage_active_per_core")
|
||||
log.Infof("%s", jsonutils.Marshal(q))
|
||||
}
|
||||
+104
-18
@@ -14,7 +14,10 @@
|
||||
|
||||
package monitor
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
UNIFIED_MONITOR_FIELD_OPT_TYPE = []string{"Aggregations", "Selectors"}
|
||||
@@ -70,23 +73,6 @@ type MetricFunc struct {
|
||||
GroupOptValue map[string][]string `json:"group_opt_value"`
|
||||
}
|
||||
|
||||
type MetricInputQuery struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Scope string `json:"scope"`
|
||||
Slimit string `json:"slimit"`
|
||||
Soffset string `json:"soffset"`
|
||||
//default group by
|
||||
Unit bool `json:"unit"`
|
||||
Interval string `json:"interval"`
|
||||
DomainId string `json:"domain_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
MetricQuery []*AlertQuery `json:"metric_query"`
|
||||
Signature string `json:"signature"`
|
||||
ShowMeta bool `json:"show_meta"`
|
||||
SkipCheckSeries bool `json:"skip_check_series"`
|
||||
}
|
||||
|
||||
type SimpleQueryInput struct {
|
||||
// 资源Id, 可以不填, 代表查询指定监控的所有监控数据
|
||||
Id string `json:"id"`
|
||||
@@ -108,3 +94,103 @@ type SimpleQueryOutput struct {
|
||||
Time time.Time `json:"time"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type MetricsQueryResult struct {
|
||||
SeriesTotal int64
|
||||
Series TimeSeriesSlice
|
||||
Metas []QueryResultMeta
|
||||
}
|
||||
|
||||
type TimeSeriesPoints []TimePoint
|
||||
|
||||
type TimeSeriesSlice []*TimeSeries
|
||||
|
||||
type TimeSeries struct {
|
||||
// RawName is used to frontend displaying the curve name
|
||||
RawName string `json:"raw_name"`
|
||||
Columns []string `json:"columns"`
|
||||
Name string `json:"name"`
|
||||
Points TimeSeriesPoints `json:"points"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
type TimePoint []interface{}
|
||||
|
||||
func NewTimePoint(value *float64, timestamp float64) TimePoint {
|
||||
return TimePoint{value, timestamp}
|
||||
}
|
||||
|
||||
func NewTimePointByVal(value float64, timestamp float64) TimePoint {
|
||||
return NewTimePoint(&value, timestamp)
|
||||
}
|
||||
|
||||
func (p TimePoint) IsValid() bool {
|
||||
if val, ok := p[0].(*float64); ok && val != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
//return p[0].(*float64) != nil
|
||||
}
|
||||
|
||||
func (p TimePoint) IsValids() bool {
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
if p[i] == nil {
|
||||
return false
|
||||
}
|
||||
if p[i].(*float64) == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p TimePoint) Value() float64 {
|
||||
return *(p[0].(*float64))
|
||||
}
|
||||
|
||||
func (p TimePoint) Timestamp() float64 {
|
||||
return p[len(p)-1].(float64)
|
||||
}
|
||||
|
||||
func (p TimePoint) Values() []float64 {
|
||||
values := make([]float64, 0)
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
values = append(values, *(p[i].(*float64)))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func (p TimePoint) PointValueStr() []string {
|
||||
arrStr := make([]string, 0)
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
if p[i] == nil {
|
||||
arrStr = append(arrStr, "")
|
||||
}
|
||||
if fval, ok := p[i].(*float64); ok {
|
||||
arrStr = append(arrStr, strconv.FormatFloat((*fval), 'f', -1, 64))
|
||||
continue
|
||||
}
|
||||
if ival, ok := p[i].(*int64); ok {
|
||||
arrStr = append(arrStr, strconv.FormatInt((*ival), 64))
|
||||
continue
|
||||
}
|
||||
arrStr = append(arrStr, p[i].(string))
|
||||
}
|
||||
return arrStr
|
||||
}
|
||||
|
||||
func NewTimeSeriesPointsFromArgs(values ...float64) TimeSeriesPoints {
|
||||
points := make(TimeSeriesPoints, 0)
|
||||
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
points = append(points, NewTimePoint(&values[i], values[i+1]))
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
type QueryResultMeta struct {
|
||||
RawQuery string `json:"raw_query"`
|
||||
}
|
||||
|
||||
const ConditionTypeMetricQuery = "metricquery"
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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 monitor
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
)
|
||||
|
||||
type MetricQueryInput struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Scope string `json:"scope"`
|
||||
Slimit string `json:"slimit"`
|
||||
Soffset string `json:"soffset"`
|
||||
Unit bool `json:"unit"`
|
||||
Interval string `json:"interval"`
|
||||
DomainId string `json:"domain_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
MetricQuery []*AlertQuery `json:"metric_query"`
|
||||
Signature string `json:"signature"`
|
||||
ShowMeta bool `json:"show_meta"`
|
||||
SkipCheckSeries bool `json:"skip_check_series"`
|
||||
}
|
||||
|
||||
const (
|
||||
QUERY_SIGNATURE_KEY = "signature"
|
||||
)
|
||||
|
||||
func DigestQuerySignature(data *jsonutils.JSONDict) string {
|
||||
data.Remove(QUERY_SIGNATURE_KEY)
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data.String())))
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package monitor
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
@@ -407,10 +408,7 @@ func (s *AlertQuerySelects) Select(fieldName string) *AlertQuerySelect {
|
||||
s.parts = make([]*AlertQuerySelect, 0)
|
||||
}
|
||||
sel := make([]monitor.MetricQueryPart, 0)
|
||||
sel = append(sel, monitor.MetricQueryPart{
|
||||
Type: "field",
|
||||
Params: []string{fieldName},
|
||||
})
|
||||
sel = append(sel, monitor.NewMetricQueryPartField(fieldName))
|
||||
part := &AlertQuerySelect{sel}
|
||||
s.parts = append(s.parts, part)
|
||||
return part
|
||||
@@ -424,58 +422,48 @@ func (s *AlertQuerySelects) ToSelects() []monitor.MetricQuerySelect {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) addFunc(funcName string) *AlertQuerySelect {
|
||||
s.MetricQuerySelect = append(s.MetricQuerySelect, monitor.MetricQueryPart{
|
||||
Type: funcName,
|
||||
})
|
||||
func (s *AlertQuerySelect) addPart(p monitor.MetricQueryPart) *AlertQuerySelect {
|
||||
s.MetricQuerySelect = append(s.MetricQuerySelect, p)
|
||||
return s
|
||||
}
|
||||
|
||||
// Aggregations method
|
||||
func (s *AlertQuerySelect) MEAN() *AlertQuerySelect {
|
||||
return s.addFunc("mean")
|
||||
return s.addPart(monitor.NewMetricQueryPartMean())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) COUNT() *AlertQuerySelect {
|
||||
return s.addFunc("count")
|
||||
return s.addPart(monitor.NewMetricQueryPartCount())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) DISTINCT() *AlertQuerySelect {
|
||||
return s.addFunc("distinct")
|
||||
return s.addPart(monitor.NewMetricQueryPartDistinct())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) SUM() *AlertQuerySelect {
|
||||
return s.addFunc("sum")
|
||||
return s.addPart(monitor.NewMetricQueryPartSum())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) MIN() *AlertQuerySelect {
|
||||
return s.addFunc("min")
|
||||
return s.addPart(monitor.NewMetricQueryPartMin())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) MAX() *AlertQuerySelect {
|
||||
return s.addFunc("max")
|
||||
return s.addPart(monitor.NewMetricQueryPartMax())
|
||||
}
|
||||
|
||||
func (s *AlertQuerySelect) LAST() *AlertQuerySelect {
|
||||
return s.addFunc("last")
|
||||
return s.addPart(monitor.NewMetricQueryPartLast())
|
||||
}
|
||||
|
||||
// AS is alias method
|
||||
func (s *AlertQuerySelect) AS(alias string) *AlertQuerySelect {
|
||||
s.MetricQuerySelect = append(s.MetricQuerySelect, monitor.MetricQueryPart{
|
||||
Type: "alias",
|
||||
Params: []string{alias},
|
||||
})
|
||||
return s
|
||||
return s.addPart(monitor.NewMetricQueryPartAS(alias))
|
||||
}
|
||||
|
||||
// MATH method
|
||||
func (s *AlertQuerySelect) MATH(op string, val string) *AlertQuerySelect {
|
||||
s.MetricQuerySelect = append(s.MetricQuerySelect, monitor.MetricQueryPart{
|
||||
Type: "math",
|
||||
Params: []string{fmt.Sprintf("%s %s", op, val)},
|
||||
})
|
||||
return s
|
||||
return s.addPart(monitor.NewMetricQueryPartMath(op, val))
|
||||
}
|
||||
|
||||
type AlertQueryWhere struct {
|
||||
@@ -595,3 +583,90 @@ func (g *AlertQueryGroupBy) FILL(val string) *AlertQueryGroupBy {
|
||||
func (g *AlertQueryGroupBy) ToGroupBy() []monitor.MetricQueryPart {
|
||||
return g.parts
|
||||
}
|
||||
|
||||
type MetricQueryInput struct {
|
||||
from string `json:"from"`
|
||||
to string `json:"to"`
|
||||
scope string `json:"scope"`
|
||||
slimit string `json:"slimit"`
|
||||
soffset string `json:"soffset"`
|
||||
unit bool `json:"unit"`
|
||||
interval string `json:"interval"`
|
||||
domainId string `json:"domain_id"`
|
||||
projectId string `json:"project_id"`
|
||||
showMeta bool `json:"show_meta"`
|
||||
skipCheckSeries bool `json:"skip_check_series"`
|
||||
query *AlertQuery
|
||||
}
|
||||
|
||||
func NewMetricQueryInput(measurement string) *MetricQueryInput {
|
||||
return NewMetricQueryInputWithDB("", measurement)
|
||||
}
|
||||
|
||||
func NewMetricQueryInputWithDB(db string, measurement string) *MetricQueryInput {
|
||||
return &MetricQueryInput{
|
||||
query: NewAlertQuery(db, measurement),
|
||||
}
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) Interval(interval string) *MetricQueryInput {
|
||||
input.interval = interval
|
||||
return input
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) From(from time.Time) *MetricQueryInput {
|
||||
input.from = fmt.Sprintf("%d", from.UnixMilli())
|
||||
return input
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) To(to time.Time) *MetricQueryInput {
|
||||
input.to = fmt.Sprintf("%d", to.UnixMilli())
|
||||
return input
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) Scope(scope string) *MetricQueryInput {
|
||||
input.scope = scope
|
||||
return input
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) SkipCheckSeries(skip bool) *MetricQueryInput {
|
||||
input.skipCheckSeries = skip
|
||||
return input
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) Selects() *AlertQuerySelects {
|
||||
return input.query.Selects()
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) Where() *AlertQueryWhere {
|
||||
return input.query.Where()
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) GroupBy() *AlertQueryGroupBy {
|
||||
return input.query.GroupBy()
|
||||
}
|
||||
|
||||
func (input *MetricQueryInput) ToQueryData() *monitor.MetricQueryInput {
|
||||
data := &monitor.MetricQueryInput{
|
||||
From: input.from,
|
||||
To: input.to,
|
||||
Scope: input.scope,
|
||||
Slimit: input.slimit,
|
||||
Soffset: input.soffset,
|
||||
Unit: input.unit,
|
||||
Interval: input.interval,
|
||||
DomainId: input.domainId,
|
||||
ProjectId: input.projectId,
|
||||
ShowMeta: input.showMeta,
|
||||
SkipCheckSeries: input.skipCheckSeries,
|
||||
}
|
||||
data.MetricQuery = []*monitor.AlertQuery{
|
||||
{Model: input.query.ToMetricQuery()},
|
||||
}
|
||||
|
||||
jsonData := jsonutils.Marshal(data).(*jsonutils.JSONDict)
|
||||
digest := monitor.DigestQuerySignature(jsonData)
|
||||
data.Signature = digest
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
@@ -44,3 +46,6 @@ func NewUnifiedMonitorManager() *SUnifiedMonitorManager {
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SUnifiedMonitorManager) PerformQuery(s *mcclient.ClientSession, input *monitor.MetricQueryInput) {
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
type mathReducer struct {
|
||||
@@ -40,7 +39,7 @@ func (s *mathReducer) GetType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s *mathReducer) Reduce(series *tsdb.TimeSeries) (*float64, []string) {
|
||||
func (s *mathReducer) Reduce(series *monitor.TimeSeries) (*float64, []string) {
|
||||
if len(series.Points) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
mq.RegisterMetricQuery("metricquery", func(model []*monitor.AlertCondition) (mq.MetricQuery, error) {
|
||||
mq.RegisterMetricQuery(monitor.ConditionTypeMetricQuery, func(model []*monitor.AlertCondition) (mq.MetricQuery, error) {
|
||||
return NewMetricQueryCondition(model)
|
||||
})
|
||||
}
|
||||
@@ -60,11 +60,11 @@ func NewMetricQueryCondition(models []*monitor.AlertCondition) (*MetricQueryCond
|
||||
qc.Query.From = q.From
|
||||
qc.Query.To = q.To
|
||||
if err := validators.ValidateFromValue(qc.Query.From); err != nil {
|
||||
return nil, errors.Wrapf(err, "from value %q", qc.Query.From)
|
||||
return nil, errors.Wrapf(err, "validate from value %q", qc.Query.From)
|
||||
}
|
||||
|
||||
if err := validators.ValidateToValue(qc.Query.To); err != nil {
|
||||
return nil, errors.Wrapf(err, "to value %q", qc.Query.To)
|
||||
return nil, errors.Wrapf(err, "validate to value %q", qc.Query.To)
|
||||
}
|
||||
qc.setResType()
|
||||
cond.QueryCons = append(cond.QueryCons, *qc)
|
||||
@@ -73,7 +73,7 @@ func NewMetricQueryCondition(models []*monitor.AlertCondition) (*MetricQueryCond
|
||||
return cond, nil
|
||||
}
|
||||
|
||||
func (query *MetricQueryCondition) ExecuteQuery(userCred mcclient.TokenCredential, skipCheckSeries bool) (*mq.Metrics, error) {
|
||||
func (query *MetricQueryCondition) ExecuteQuery(userCred mcclient.TokenCredential, skipCheckSeries bool) (*monitor.MetricsQueryResult, error) {
|
||||
firstCond := query.QueryCons[0]
|
||||
timeRange := tsdb.NewTimeRange(firstCond.Query.From, firstCond.Query.To)
|
||||
ctx := gocontext.Background()
|
||||
@@ -87,23 +87,29 @@ func (query *MetricQueryCondition) ExecuteQuery(userCred mcclient.TokenCredentia
|
||||
var qr *queryResult
|
||||
var err error
|
||||
|
||||
queryInfluxdb := func() (*queryResult, error) {
|
||||
ds, err := datasource.GetDefaultSource("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Can't find default datasource")
|
||||
}
|
||||
|
||||
queryTSDB := func() (*queryResult, error) {
|
||||
startTime := time.Now()
|
||||
queryResult, err := query.executeQuery(&evalContext, timeRange)
|
||||
|
||||
queryResult, err := query.executeQuery(ds, &evalContext, timeRange)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "query.executeQuery")
|
||||
return nil, errors.Wrapf(err, "query.executeQuery from %s", ds.Type)
|
||||
}
|
||||
log.Debugf("query metrics from influxdb elapsed: %s", time.Since(startTime))
|
||||
log.Debugf("query metrics from TSDB %q elapsed: %s", ds.Type, time.Since(startTime))
|
||||
return queryResult, nil
|
||||
}
|
||||
|
||||
if query.noCheckSeries(skipCheckSeries) {
|
||||
qr, err = queryInfluxdb()
|
||||
qr, err = queryTSDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "queryTSDB")
|
||||
}
|
||||
metrics := mq.Metrics{
|
||||
Series: make(tsdb.TimeSeriesSlice, 0),
|
||||
metrics := monitor.MetricsQueryResult{
|
||||
Series: make(monitor.TimeSeriesSlice, 0),
|
||||
Metas: qr.metas,
|
||||
}
|
||||
metrics.Series = qr.series
|
||||
@@ -114,7 +120,7 @@ func (query *MetricQueryCondition) ExecuteQuery(userCred mcclient.TokenCredentia
|
||||
qRegionCh := make(chan bool, 0)
|
||||
|
||||
go func() {
|
||||
qr, err = queryInfluxdb()
|
||||
qr, err = queryTSDB()
|
||||
qInfluxdbCh <- true
|
||||
}()
|
||||
|
||||
@@ -163,8 +169,8 @@ func (query *MetricQueryCondition) ExecuteQuery(userCred mcclient.TokenCredentia
|
||||
// firstCond.FillSerieByResourceField(resource, serie)
|
||||
// metrics.Series = append(metrics.Series, serie)
|
||||
//}
|
||||
metrics := mq.Metrics{
|
||||
Series: make(tsdb.TimeSeriesSlice, 0),
|
||||
metrics := monitor.MetricsQueryResult{
|
||||
Series: make(monitor.TimeSeriesSlice, 0),
|
||||
Metas: qr.metas,
|
||||
}
|
||||
mtx := sync.Mutex{}
|
||||
@@ -217,15 +223,10 @@ func (query *MetricQueryCondition) noCheckSeries(skipCheckSeries bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *MetricQueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (*queryResult, error) {
|
||||
ds, err := datasource.GetDefaultSource("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Can't find default datasource")
|
||||
}
|
||||
|
||||
func (c *MetricQueryCondition) executeQuery(ds *tsdb.DataSource, context *alerting.EvalContext, timeRange *tsdb.TimeRange) (*queryResult, error) {
|
||||
req := c.getRequestQuery(ds, timeRange, context.IsDebug)
|
||||
result := make(tsdb.TimeSeriesSlice, 0)
|
||||
metas := make([]tsdb.QueryResultMeta, 0)
|
||||
result := make(monitor.TimeSeriesSlice, 0)
|
||||
metas := make([]monitor.QueryResultMeta, 0)
|
||||
|
||||
if context.IsDebug {
|
||||
setContextLog(context, req)
|
||||
|
||||
@@ -66,7 +66,7 @@ type AlertQuery struct {
|
||||
}
|
||||
|
||||
type FormatCond struct {
|
||||
QueryMeta *tsdb.QueryResultMeta
|
||||
QueryMeta *monitor.QueryResultMeta
|
||||
QueryKeyInfo string
|
||||
Reducer string
|
||||
Evaluator AlertEvaluator
|
||||
@@ -93,7 +93,7 @@ func GetFetchImpByDb(db string) iEvalMatchFetch {
|
||||
return iFetchImp[db]
|
||||
}
|
||||
|
||||
func (c *QueryCondition) GenerateFormatCond(meta *tsdb.QueryResultMeta, metric string) *FormatCond {
|
||||
func (c *QueryCondition) GenerateFormatCond(meta *monitor.QueryResultMeta, metric string) *FormatCond {
|
||||
return &FormatCond{
|
||||
QueryMeta: meta,
|
||||
QueryKeyInfo: metric,
|
||||
@@ -186,7 +186,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio
|
||||
if evalMatch {
|
||||
evalMatchCount++
|
||||
}
|
||||
var meta *tsdb.QueryResultMeta
|
||||
var meta *monitor.QueryResultMeta
|
||||
if len(metas) > 0 {
|
||||
//the relation metas with series is 1 to more
|
||||
meta = &metas[0]
|
||||
@@ -239,7 +239,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *QueryCondition) serieIsLatestResource(resources map[string]jsonutils.JSONObject, series *tsdb.TimeSeries) (bool, jsonutils.JSONObject) {
|
||||
func (c *QueryCondition) serieIsLatestResource(resources map[string]jsonutils.JSONObject, series *monitor.TimeSeries) (bool, jsonutils.JSONObject) {
|
||||
tagId := monitor.MEASUREMENT_TAG_ID[c.ResType]
|
||||
if len(tagId) == 0 {
|
||||
tagId = "host_id"
|
||||
@@ -263,7 +263,7 @@ func (c *QueryCondition) serieIsLatestResource(resources map[string]jsonutils.JS
|
||||
}
|
||||
|
||||
func (c *QueryCondition) FillSerieByResourceField(resource jsonutils.JSONObject,
|
||||
series *tsdb.TimeSeries) {
|
||||
series *monitor.TimeSeries) {
|
||||
//startTime := time.Now()
|
||||
//defer func() {
|
||||
// log.Debugf("--FillSerieByResourceField: %s", time.Since(startTime))
|
||||
@@ -278,8 +278,8 @@ func (c *QueryCondition) FillSerieByResourceField(resource jsonutils.JSONObject,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb.TimeSeries,
|
||||
meta *tsdb.QueryResultMeta, value *float64, valStrArr []string) (*monitor.EvalMatch, error) {
|
||||
func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series monitor.TimeSeries,
|
||||
meta *monitor.QueryResultMeta, value *float64, valStrArr []string) (*monitor.EvalMatch, error) {
|
||||
evalMatch := new(monitor.EvalMatch)
|
||||
alertDetails, err := c.GetCommonAlertDetails(context)
|
||||
if err != nil {
|
||||
@@ -351,7 +351,7 @@ func (c *QueryCondition) GetCommonAlertDetails(context *alerting.EvalContext) (*
|
||||
return alertDetails, nil
|
||||
}
|
||||
|
||||
func (c *QueryCondition) jointPointStr(series tsdb.TimeSeries, value string, valStrArr []string) string {
|
||||
func (c *QueryCondition) jointPointStr(series monitor.TimeSeries, value string, valStrArr []string) string {
|
||||
str := ""
|
||||
for i := 0; i < len(valStrArr); i++ {
|
||||
if i == 0 {
|
||||
@@ -364,14 +364,14 @@ func (c *QueryCondition) jointPointStr(series tsdb.TimeSeries, value string, val
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
series tsdb.TimeSeriesSlice
|
||||
metas []tsdb.QueryResultMeta
|
||||
series monitor.TimeSeriesSlice
|
||||
metas []monitor.QueryResultMeta
|
||||
}
|
||||
|
||||
func (c *QueryCondition) executeQuery(evalCtx *alerting.EvalContext, timeRange *tsdb.TimeRange) (*queryResult, error) {
|
||||
req := c.getRequestForAlertRule(timeRange, evalCtx.IsDebug)
|
||||
result := make(tsdb.TimeSeriesSlice, 0)
|
||||
metas := make([]tsdb.QueryResultMeta, 0)
|
||||
result := make(monitor.TimeSeriesSlice, 0)
|
||||
metas := make([]monitor.QueryResultMeta, 0)
|
||||
|
||||
if evalCtx.IsDebug {
|
||||
data := jsonutils.NewDict()
|
||||
|
||||
@@ -22,12 +22,11 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
"yunion.io/x/onecloud/pkg/monitor/validators"
|
||||
)
|
||||
|
||||
type Reducer interface {
|
||||
Reduce(series *tsdb.TimeSeries) (*float64, []string)
|
||||
Reduce(series *monitor.TimeSeries) (*float64, []string)
|
||||
GetType() string
|
||||
GetParams() []float64
|
||||
}
|
||||
@@ -48,7 +47,7 @@ func (s *queryReducer) GetType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
func (s *queryReducer) Reduce(series *tsdb.TimeSeries) (*float64, []string) {
|
||||
func (s *queryReducer) Reduce(series *monitor.TimeSeries) (*float64, []string) {
|
||||
if len(series.Points) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -181,7 +180,7 @@ func newSimpleReducerByType(typ string) *queryReducer {
|
||||
}
|
||||
}
|
||||
|
||||
func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
|
||||
func calculateDiff(series *monitor.TimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
|
||||
var (
|
||||
points = series.Points
|
||||
first float64
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
)
|
||||
|
||||
func TestSimpleReducer(t *testing.T) {
|
||||
@@ -67,16 +67,16 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
Convey("median should ignore null values", func() {
|
||||
reducer := newSimpleReducerByType("median")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time series",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 3))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(1, 4))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(2, 5))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(3, 6))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 3))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(1, 4))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(2, 5))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(3, 6))
|
||||
|
||||
result, _ := reducer.Reduce(series)
|
||||
So(result, ShouldNotBeNil)
|
||||
@@ -91,14 +91,14 @@ func TestSimpleReducer(t *testing.T) {
|
||||
Convey("count_non_null", func() {
|
||||
Convey("with null values and real values", func() {
|
||||
reducer := newSimpleReducerByType("count_non_null")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time series",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(3, 3))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(4, 4))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(3, 3))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(4, 4))
|
||||
reduce, _ := reducer.Reduce(series)
|
||||
So(reduce, ShouldNotBeNil)
|
||||
So(*reduce, ShouldEqual, 2)
|
||||
@@ -106,12 +106,12 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
Convey("with null values", func() {
|
||||
reducer := newSimpleReducerByType("count_non_null")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time series",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
reduce, _ := reducer.Reduce(series)
|
||||
So(reduce, ShouldBeNil)
|
||||
})
|
||||
@@ -119,14 +119,14 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
Convey("avg of number values and null values should ignore nulls", func() {
|
||||
reduer := newSimpleReducerByType("avg")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time series",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 3))
|
||||
series.Points = append(series.Points, tsdb.NewTimePointByVal(3, 4))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 3))
|
||||
series.Points = append(series.Points, monitor.NewTimePointByVal(3, 4))
|
||||
reduce, _ := reduer.Reduce(series)
|
||||
So(*reduce, ShouldEqual, 3)
|
||||
})
|
||||
@@ -148,12 +148,12 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
Convey("diff with only nulls", func() {
|
||||
reducer := newSimpleReducerByType("diff")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time serie",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
reduce, _ := reducer.Reduce(series)
|
||||
So(reduce, ShouldBeNil)
|
||||
})
|
||||
@@ -175,12 +175,12 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
Convey("percent_diff with only nulls", func() {
|
||||
reducer := newSimpleReducerByType("percent_diff")
|
||||
series := &tsdb.TimeSeries{
|
||||
series := &monitor.TimeSeries{
|
||||
Name: "test time serie",
|
||||
}
|
||||
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, tsdb.NewTimePoint(nil, 2))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 1))
|
||||
series.Points = append(series.Points, monitor.NewTimePoint(nil, 2))
|
||||
reduce, _ := reducer.Reduce(series)
|
||||
So(reduce, ShouldBeNil)
|
||||
})
|
||||
@@ -189,13 +189,13 @@ func TestSimpleReducer(t *testing.T) {
|
||||
|
||||
func testReducer(reducerType string, datapoints ...float64) float64 {
|
||||
reducer := newSimpleReducerByType(reducerType)
|
||||
serires := &tsdb.TimeSeries{
|
||||
serires := &monitor.TimeSeries{
|
||||
Name: "test time series",
|
||||
}
|
||||
|
||||
for idx := range datapoints {
|
||||
val := datapoints[idx]
|
||||
serires.Points = append(serires.Points, tsdb.NewTimePoint(&val, 1234134))
|
||||
serires.Points = append(serires.Points, monitor.NewTimePoint(&val, 1234134))
|
||||
}
|
||||
reduce, _ := reducer.Reduce(serires)
|
||||
return *reduce
|
||||
|
||||
@@ -17,7 +17,7 @@ package conditions
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
)
|
||||
|
||||
type suggestRuleReducer struct {
|
||||
@@ -32,7 +32,7 @@ func NewSuggestRuleReducer(t string, duration time.Duration) Reducer {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *suggestRuleReducer) Reduce(series *tsdb.TimeSeries) (*float64, []string) {
|
||||
func (s *suggestRuleReducer) Reduce(series *monitor.TimeSeries) (*float64, []string) {
|
||||
/*if int(s.duration.Seconds()) > len(series.Points) {
|
||||
return nil, nil
|
||||
}*/
|
||||
|
||||
@@ -17,17 +17,10 @@ package metricquery
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
type Metrics struct {
|
||||
SeriesTotal int64
|
||||
Series tsdb.TimeSeriesSlice
|
||||
Metas []tsdb.QueryResultMeta
|
||||
}
|
||||
|
||||
type MetricQuery interface {
|
||||
ExecuteQuery(userCred mcclient.TokenCredential, forceCheckSeries bool) (*Metrics, error)
|
||||
ExecuteQuery(userCred mcclient.TokenCredential, forceCheckSeries bool) (*monitor.MetricsQueryResult, error)
|
||||
}
|
||||
|
||||
type QueryFactory func(model []*monitor.AlertCondition) (MetricQuery, error)
|
||||
|
||||
@@ -292,13 +292,13 @@ func (man *SCommonAlertManager) genName(ctx context.Context, ownerId mcclient.II
|
||||
|
||||
func (man *SCommonAlertManager) ValidateMetricQuery(metricRequest *monitor.CommonMetricInputQuery, scope string, ownerId mcclient.IIdentityProvider) error {
|
||||
for _, q := range metricRequest.MetricQuery {
|
||||
metriInputQuery := monitor.MetricInputQuery{
|
||||
metriInputQuery := monitor.MetricQueryInput{
|
||||
From: metricRequest.From,
|
||||
To: metricRequest.To,
|
||||
Interval: metricRequest.Interval,
|
||||
}
|
||||
setDefaultValue(q.AlertQuery, &metriInputQuery, scope, ownerId)
|
||||
err := UnifiedMonitorManager.ValidateInputQuery(q.AlertQuery)
|
||||
err := UnifiedMonitorManager.ValidateInputQuery(q.AlertQuery, &metriInputQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -177,12 +177,7 @@ func (self *SDataSourceManager) GetMeasurementsWithDescriptionInfos(query jsonut
|
||||
if err != nil {
|
||||
return jsonutils.JSONNull, errors.Wrap(err, "getInfluxdbMeasurements")
|
||||
}
|
||||
dataSource, err := datasource.GetDefaultSource("")
|
||||
if err != nil {
|
||||
return jsonutils.JSONNull, errors.Wrap(err, "s.GetDefaultSource")
|
||||
}
|
||||
db := influxdb.NewInfluxdb(dataSource.Url)
|
||||
filterMeasurements, err := self.filterMeasurementsByTime(*db, measurements, query, tagFilter)
|
||||
filterMeasurements, err := self.filterMeasurementsByTime(measurements, query, tagFilter)
|
||||
if err != nil {
|
||||
return jsonutils.JSONNull, errors.Wrap(err, "filterMeasurementsByTime error")
|
||||
}
|
||||
@@ -325,13 +320,13 @@ type influxdbQueryChan struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (self *SDataSourceManager) filterMeasurementsByTime(db influxdb.SInfluxdb,
|
||||
func (self *SDataSourceManager) filterMeasurementsByTime(
|
||||
measurements []monitor.InfluxMeasurement, query jsonutils.JSONObject, tagFilter *monitor.MetricQueryTag) ([]monitor.InfluxMeasurement, error) {
|
||||
timeF, err := self.getFromAndToFromParam(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filterMeasurements, err := self.getFilterMeasurementsAsync(timeF.From, timeF.To, measurements, db, tagFilter)
|
||||
filterMeasurements, err := self.getFilterMeasurementsAsync(timeF.From, timeF.To, measurements, tagFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -367,7 +362,7 @@ func (self *SDataSourceManager) getFromAndToFromParam(query jsonutils.JSONObject
|
||||
}
|
||||
|
||||
func (self *SDataSourceManager) getFilterMeasurementsAsync(from, to string,
|
||||
measurements []monitor.InfluxMeasurement, db influxdb.SInfluxdb, tagFilter *monitor.MetricQueryTag) ([]monitor.InfluxMeasurement, error) {
|
||||
measurements []monitor.InfluxMeasurement, tagFilter *monitor.MetricQueryTag) ([]monitor.InfluxMeasurement, error) {
|
||||
filterMeasurements := make([]monitor.InfluxMeasurement, 0)
|
||||
queryChan := new(influxdbQueryChan)
|
||||
queryChan.queryRtnChan = make(chan monitor.InfluxMeasurement, len(measurements))
|
||||
@@ -379,7 +374,7 @@ func (self *SDataSourceManager) getFilterMeasurementsAsync(from, to string,
|
||||
for i, _ := range measurements {
|
||||
tmp := measurements[i]
|
||||
measurementQueryGroup.Go(func() error {
|
||||
return self.getFilterMeasurement(queryChan, from, to, tmp, db, tagFilter)
|
||||
return self.getFilterMeasurement(queryChan, from, to, tmp, tagFilter)
|
||||
})
|
||||
}
|
||||
measurementQueryGroup.Go(func() error {
|
||||
@@ -399,7 +394,7 @@ func (self *SDataSourceManager) getFilterMeasurementsAsync(from, to string,
|
||||
return filterMeasurements, err
|
||||
}
|
||||
|
||||
func (self *SDataSourceManager) getFilterMeasurement(queryChan *influxdbQueryChan, from, to string, measurement monitor.InfluxMeasurement, db influxdb.SInfluxdb, tagFilter *monitor.MetricQueryTag) error {
|
||||
func (self *SDataSourceManager) getFilterMeasurement(queryChan *influxdbQueryChan, from, to string, measurement monitor.InfluxMeasurement, tagFilter *monitor.MetricQueryTag) error {
|
||||
dds, _ := datasource.GetDefaultSource("")
|
||||
ep, err := datasource.GetDefaultQueryEndpoint()
|
||||
if err != nil {
|
||||
@@ -730,7 +725,7 @@ func getTagValues(userCred mcclient.TokenCredential, output *monitor.InfluxMeasu
|
||||
To: timeF.To,
|
||||
}
|
||||
|
||||
q := monitor.MetricInputQuery{
|
||||
q := monitor.MetricQueryInput{
|
||||
From: timeF.From,
|
||||
To: timeF.To,
|
||||
MetricQuery: []*monitor.AlertQuery{
|
||||
|
||||
@@ -15,38 +15,26 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
const (
|
||||
QUERY_SIGNATURE_KEY = "signature"
|
||||
)
|
||||
|
||||
func digestQuerySignature(data *jsonutils.JSONDict) string {
|
||||
data.Remove(QUERY_SIGNATURE_KEY)
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data.String())))
|
||||
}
|
||||
|
||||
func ValidateQuerySignature(input jsonutils.JSONObject) error {
|
||||
data, ok := input.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return httperrors.NewInputParameterError("input not json dict")
|
||||
}
|
||||
signature, err := data.GetString(QUERY_SIGNATURE_KEY)
|
||||
signature, err := data.GetString(monitor.QUERY_SIGNATURE_KEY)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == jsonutils.ErrJsonDictKeyNotFound {
|
||||
return httperrors.NewNotFoundError("not found signature")
|
||||
}
|
||||
return errors.Wrap(err, "get signature")
|
||||
}
|
||||
if signature != digestQuerySignature(data) {
|
||||
|
||||
if signature != monitor.DigestQuerySignature(data) {
|
||||
return httperrors.NewBadRequestError("signature error")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
)
|
||||
|
||||
func Test_digestQuerySignature(t *testing.T) {
|
||||
@@ -42,7 +44,7 @@ func Test_digestQuerySignature(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := digestQuerySignature(tt.data); got != tt.want {
|
||||
if got := monitor.DigestQuerySignature(tt.data); got != tt.want {
|
||||
t.Errorf("sumQuerySignature() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"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"
|
||||
@@ -34,13 +33,11 @@ import (
|
||||
"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"
|
||||
mod "yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
|
||||
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 (
|
||||
@@ -200,7 +197,7 @@ func (self *SUnifiedMonitorManager) SetHandlerProcessTimeout(info *appsrv.SHandl
|
||||
return 5 * time.Minute
|
||||
}
|
||||
|
||||
func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (*monitor.MetricsQueryResult, error) {
|
||||
tmp := jsonutils.DeepCopy(data)
|
||||
self.handleDataPreSignature(ctx, tmp)
|
||||
if !options.Options.DisableQuerySignatureCheck {
|
||||
@@ -208,10 +205,10 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m
|
||||
return nil, errors.Wrap(err, "ValidateQuerySignature")
|
||||
}
|
||||
}
|
||||
inputQuery := new(monitor.MetricInputQuery)
|
||||
inputQuery := new(monitor.MetricQueryInput)
|
||||
err := data.Unmarshal(inputQuery)
|
||||
if err != nil {
|
||||
return jsonutils.NewDict(), err
|
||||
return nil, err
|
||||
}
|
||||
if len(inputQuery.MetricQuery) == 0 {
|
||||
return nil, merrors.NewArgIsEmptyErr("metric_query")
|
||||
@@ -223,8 +220,8 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m
|
||||
ownId = userCred
|
||||
}
|
||||
setDefaultValue(q, inputQuery, scope, ownId)
|
||||
if err := self.ValidateInputQuery(q); err != nil {
|
||||
return jsonutils.NewDict(), errors.Wrapf(err, "ValidateInputQuery")
|
||||
if err := self.ValidateInputQuery(q, inputQuery); err != nil {
|
||||
return nil, errors.Wrapf(err, "ValidateInputQuery")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,9 +234,13 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m
|
||||
}
|
||||
}
|
||||
|
||||
return self.performQuery(ctx, userCred, inputQuery)
|
||||
}
|
||||
|
||||
func (self *SUnifiedMonitorManager) performQuery(ctx context.Context, userCred mcclient.TokenCredential, inputQuery *monitor.MetricQueryInput) (*monitor.MetricsQueryResult, error) {
|
||||
rtn, err := doQuery(userCred, *inputQuery)
|
||||
if err != nil {
|
||||
return jsonutils.NewDict(), errors.Wrapf(err, "doQuery with input %s", data)
|
||||
return nil, errors.Wrapf(err, "doQuery with input %s", jsonutils.Marshal(inputQuery))
|
||||
}
|
||||
|
||||
if len(inputQuery.Soffset) != 0 && len(inputQuery.Slimit) != 0 {
|
||||
@@ -268,14 +269,14 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m
|
||||
}
|
||||
|
||||
fillSerieTags(&rtn.Series)
|
||||
return jsonutils.Marshal(rtn), nil
|
||||
return 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 := new(monitor.MetricQueryInput)
|
||||
forkInputQury.MetricQuery = []*monitor.AlertQuery{&fork}
|
||||
rtn, err := doQuery(userCred, *forkInputQury)
|
||||
if err != nil {
|
||||
@@ -314,17 +315,26 @@ func (self *SUnifiedMonitorManager) handleDataPreSignature(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
func doQuery(userCred mcclient.TokenCredential, query monitor.MetricInputQuery) (*mq.Metrics, error) {
|
||||
conditions := make([]*monitor.AlertCondition, 0)
|
||||
func doQuery(userCred mcclient.TokenCredential, query monitor.MetricQueryInput) (*monitor.MetricsQueryResult, error) {
|
||||
conds := make([]*monitor.AlertCondition, 0)
|
||||
for _, q := range query.MetricQuery {
|
||||
if q.To == "" {
|
||||
q.To = query.To
|
||||
}
|
||||
if q.From == "" {
|
||||
q.From = query.From
|
||||
}
|
||||
if q.Model.Interval == "" {
|
||||
q.Model.Interval = query.Interval
|
||||
}
|
||||
condition := monitor.AlertCondition{
|
||||
Type: "metricquery",
|
||||
Type: monitor.ConditionTypeMetricQuery,
|
||||
Query: *q,
|
||||
}
|
||||
conditions = append(conditions, &condition)
|
||||
conds = append(conds, &condition)
|
||||
}
|
||||
factory := mq.GetQueryFactories()["metricquery"]
|
||||
metricQ, err := factory(conditions)
|
||||
factory := mq.GetQueryFactories()[monitor.ConditionTypeMetricQuery]
|
||||
metricQ, err := factory(conds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "factory")
|
||||
}
|
||||
@@ -340,15 +350,25 @@ func doQuery(userCred mcclient.TokenCredential, query monitor.MetricInputQuery)
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func (self *SUnifiedMonitorManager) ValidateInputQuery(query *monitor.AlertQuery) error {
|
||||
func (self *SUnifiedMonitorManager) ValidateInputQuery(query *monitor.AlertQuery, input *monitor.MetricQueryInput) error {
|
||||
if input.From == "" {
|
||||
input.From = "1h"
|
||||
}
|
||||
if input.To == "" {
|
||||
input.To = "now"
|
||||
}
|
||||
if input.Interval == "" {
|
||||
input.Interval = "5m"
|
||||
}
|
||||
|
||||
if query.From == "" {
|
||||
query.From = "1h"
|
||||
query.From = input.From
|
||||
}
|
||||
if query.Model.Interval == "" {
|
||||
query.Model.Interval = "5m"
|
||||
query.Model.Interval = input.Interval
|
||||
}
|
||||
if query.To == "" {
|
||||
query.To = "now"
|
||||
query.To = input.To
|
||||
}
|
||||
if _, err := time.ParseDuration(query.Model.Interval); err != nil {
|
||||
return httperrors.NewInputParameterError("Invalid interval format: %s", query.Model.Interval)
|
||||
@@ -356,7 +376,7 @@ 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.MetricQueryInput,
|
||||
scope string, ownerId mcclient.IIdentityProvider) {
|
||||
query.From = inputQuery.From
|
||||
query.To = inputQuery.To
|
||||
@@ -457,7 +477,7 @@ func setDefaultValue(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQ
|
||||
}
|
||||
}
|
||||
|
||||
func checkQueryGroupBy(query *monitor.AlertQuery, inputQuery *monitor.MetricInputQuery) {
|
||||
func checkQueryGroupBy(query *monitor.AlertQuery, inputQuery *monitor.MetricQueryInput) {
|
||||
if len(query.Model.GroupBy) != 0 {
|
||||
return
|
||||
}
|
||||
@@ -479,7 +499,7 @@ func checkQueryGroupBy(query *monitor.AlertQuery, inputQuery *monitor.MetricInpu
|
||||
})
|
||||
}
|
||||
|
||||
func fillSerieTags(series *tsdb.TimeSeriesSlice) {
|
||||
func fillSerieTags(series *monitor.TimeSeriesSlice) {
|
||||
for i, serie := range *series {
|
||||
for _, tag := range []string{"brand", "platform", "hypervisor"} {
|
||||
if val, ok := serie.Tags[tag]; ok {
|
||||
@@ -497,8 +517,7 @@ func fillSerieTags(series *tsdb.TimeSeriesSlice) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SUnifiedMonitorManager) GetPropertySimpleQuery(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
input *monitor.SimpleQueryInput) (jsonutils.JSONObject, error) {
|
||||
func (self *SUnifiedMonitorManager) GetPropertySimpleQuery(ctx context.Context, userCred mcclient.TokenCredential, input *monitor.SimpleQueryInput) (jsonutils.JSONObject, error) {
|
||||
if len(input.Database) == 0 {
|
||||
input.Database = "telegraf"
|
||||
}
|
||||
@@ -510,12 +529,13 @@ func (self *SUnifiedMonitorManager) GetPropertySimpleQuery(ctx context.Context,
|
||||
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{}
|
||||
}
|
||||
|
||||
data := mod.NewMetricQueryInputWithDB(input.Database, measurement).SkipCheckSeries(true)
|
||||
data.Selects().Select(field)
|
||||
|
||||
where := data.Where()
|
||||
if len(input.Id) > 0 {
|
||||
input.Tags["id"] = input.Id
|
||||
where.Equal("id", input.Id)
|
||||
}
|
||||
if input.EndTime.IsZero() {
|
||||
input.EndTime = time.Now()
|
||||
@@ -526,57 +546,40 @@ func (self *SUnifiedMonitorManager) GetPropertySimpleQuery(ctx context.Context,
|
||||
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))
|
||||
where.Equal(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)
|
||||
data.From(input.StartTime).To(input.EndTime).Interval("5m")
|
||||
|
||||
queryData := data.ToQueryData()
|
||||
dbRtn, err := self.performQuery(ctx, userCred, queryData)
|
||||
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")
|
||||
return nil, errors.Wrapf(err, "performQuery with data: %s", queryData)
|
||||
}
|
||||
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,
|
||||
})
|
||||
|
||||
for _, s := range dbRtn.Series {
|
||||
id, ok := s.Tags["id"]
|
||||
if !ok {
|
||||
log.Warningf("Not found id from series: %s", jsonutils.Marshal(s))
|
||||
continue
|
||||
}
|
||||
for _, point := range s.Points {
|
||||
if len(point) != 2 {
|
||||
log.Warningf("invalid series: %s", jsonutils.Marshal(s))
|
||||
break
|
||||
}
|
||||
timestamp := point[len(point)-1]
|
||||
valPtr, ok := point[0].(*float64)
|
||||
if !ok || valPtr == nil {
|
||||
log.Warningf("invalid series point: %#v", point)
|
||||
break
|
||||
}
|
||||
ret = append(ret, monitor.SimpleQueryOutput{
|
||||
Id: id,
|
||||
Time: time.UnixMilli(int64(timestamp.(float64))),
|
||||
Value: *valPtr,
|
||||
})
|
||||
}
|
||||
}
|
||||
return jsonutils.Marshal(map[string]interface{}{"values": ret}), nil
|
||||
|
||||
@@ -39,7 +39,6 @@ import (
|
||||
sub "yunion.io/x/onecloud/pkg/monitor/influxdbsubscribe"
|
||||
"yunion.io/x/onecloud/pkg/monitor/models"
|
||||
"yunion.io/x/onecloud/pkg/monitor/registry"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -250,13 +249,13 @@ func getQueryEvalType(evalType string) string {
|
||||
}
|
||||
|
||||
func (self *SSubscriptionManager) getPointsByAlertDetail(details monitor.CommonAlertMetricDetails, alert models.SCommonAlert,
|
||||
points []sub.Point) *tsdb.TimeSeries {
|
||||
points []sub.Point) *monitor.TimeSeries {
|
||||
metricPoints := make([]sub.Point, 0)
|
||||
|
||||
serie := tsdb.TimeSeries{
|
||||
serie := monitor.TimeSeries{
|
||||
RawName: "",
|
||||
Name: "",
|
||||
Points: make(tsdb.TimeSeriesPoints, 0),
|
||||
Points: make(monitor.TimeSeriesPoints, 0),
|
||||
Tags: nil,
|
||||
}
|
||||
|
||||
@@ -297,7 +296,7 @@ func (self *SSubscriptionManager) getPointsByAlertDetail(details monitor.CommonA
|
||||
for _, metricPoint := range metricPoints {
|
||||
if len(model.Selects) > 1 {
|
||||
fieldMap := metricPoint.Fields()
|
||||
point := make(tsdb.TimePoint, 0)
|
||||
point := make(monitor.TimePoint, 0)
|
||||
for _, sel := range model.Selects {
|
||||
point = append(point, parseValue(fieldMap[sel[0].Params[0]]))
|
||||
}
|
||||
@@ -311,7 +310,7 @@ func (self *SSubscriptionManager) getPointsByAlertDetail(details monitor.CommonA
|
||||
for fieldPoint.Next() {
|
||||
if string(fieldPoint.FieldKey()) == details.Field && isValid(fieldPoint) {
|
||||
val := fieldPoint.FloatValue()
|
||||
timePoint := tsdb.NewTimePoint(&val, float64(metricPoint.UnixNano()))
|
||||
timePoint := monitor.NewTimePoint(&val, float64(metricPoint.UnixNano()))
|
||||
serie.Points = append(serie.Points, timePoint)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func (e *InfluxdbExecutor) Query(ctx context.Context, dsInfo *tsdb.DataSource, t
|
||||
}
|
||||
for i, query := range tsdbQuery.Queries {
|
||||
ret := e.ResponseParser.Parse(&response, influxQ[i])
|
||||
ret.Meta = tsdb.QueryResultMeta{
|
||||
ret.Meta = monitor.QueryResultMeta{
|
||||
RawQuery: rawQuery,
|
||||
}
|
||||
result.Results[query.RefId] = ret
|
||||
|
||||
@@ -101,12 +101,20 @@ func (query *Query) renderTimeFilter(queryCtx *tsdb.TsdbQuery) string {
|
||||
if strings.Contains(queryCtx.TimeRange.From, "now-") {
|
||||
from = "now() - " + strings.Replace(queryCtx.TimeRange.From, "now-", "", 1)
|
||||
} else {
|
||||
from = "now() - " + queryCtx.TimeRange.From
|
||||
if _, ok := tsdb.TryParseUnixMsEpoch(queryCtx.TimeRange.From); ok {
|
||||
from = fmt.Sprintf("%sms", queryCtx.TimeRange.From)
|
||||
} else {
|
||||
from = "now() - " + queryCtx.TimeRange.From
|
||||
}
|
||||
}
|
||||
to := ""
|
||||
|
||||
if queryCtx.TimeRange.To != "now" && queryCtx.TimeRange.To != "" {
|
||||
to = " and time < now() - " + strings.Replace(queryCtx.TimeRange.To, "now-", "", 1)
|
||||
if _, ok := tsdb.TryParseUnixMsEpoch(queryCtx.TimeRange.To); ok {
|
||||
to = fmt.Sprintf(" and time < %sms", queryCtx.TimeRange.To)
|
||||
} else {
|
||||
to = " and time < now() - " + strings.Replace(queryCtx.TimeRange.To, "now-", "", 1)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("time > %s%s", from, to)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package influxdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -159,6 +160,14 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
|
||||
queryContext := &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("10m", "now")}
|
||||
So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 10m")
|
||||
})
|
||||
|
||||
Convey("render from: 1701957983540 to 1701961583540", func() {
|
||||
query := Query{}
|
||||
start := "1701957983540"
|
||||
end := "1701961583540"
|
||||
queryContext := &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange(start, end)}
|
||||
So(query.renderTimeFilter(queryContext), ShouldEqual, fmt.Sprintf("time > %sms and time < %sms", start, end))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("can render normal tags without operator", func() {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
@@ -47,22 +48,22 @@ func (rp *ResponseParser) Parse(response *Response, query *Query) *tsdb.QueryRes
|
||||
return queryRes
|
||||
}
|
||||
|
||||
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult, query *Query) tsdb.TimeSeriesSlice {
|
||||
var result tsdb.TimeSeriesSlice
|
||||
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult, query *Query) monitor.TimeSeriesSlice {
|
||||
var result monitor.TimeSeriesSlice
|
||||
for _, row := range rows {
|
||||
for columnIndex, column := range row.Columns {
|
||||
if column == "time" {
|
||||
continue
|
||||
}
|
||||
|
||||
var points tsdb.TimeSeriesPoints
|
||||
var points monitor.TimeSeriesPoints
|
||||
for _, valuePair := range row.Values {
|
||||
point, err := rp.parseTimepoint(valuePair, columnIndex)
|
||||
if err == nil {
|
||||
points = append(points, point)
|
||||
}
|
||||
}
|
||||
result = append(result, &tsdb.TimeSeries{
|
||||
result = append(result, &monitor.TimeSeries{
|
||||
Name: rp.formatSerieName(row, column, query),
|
||||
Points: points,
|
||||
Tags: row.Tags,
|
||||
@@ -73,8 +74,8 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
|
||||
return result
|
||||
}
|
||||
|
||||
func (rp *ResponseParser) transformRowsV2(rows []Row, queryResult *tsdb.QueryResult, query *Query) tsdb.TimeSeriesSlice {
|
||||
var result tsdb.TimeSeriesSlice
|
||||
func (rp *ResponseParser) transformRowsV2(rows []Row, queryResult *tsdb.QueryResult, query *Query) monitor.TimeSeriesSlice {
|
||||
var result monitor.TimeSeriesSlice
|
||||
for idx, row := range rows {
|
||||
col := ""
|
||||
columns := make([]string, 0)
|
||||
@@ -90,7 +91,7 @@ func (rp *ResponseParser) transformRowsV2(rows []Row, queryResult *tsdb.QueryRes
|
||||
col = fmt.Sprintf("%s-%s", col, column)
|
||||
}
|
||||
columns = append(columns, "time")
|
||||
var points tsdb.TimeSeriesPoints
|
||||
var points monitor.TimeSeriesPoints
|
||||
for _, valuePair := range row.Values {
|
||||
point, err := rp.parseTimepointV2(valuePair)
|
||||
if err == nil {
|
||||
@@ -192,27 +193,27 @@ func (rp *ResponseParser) buildSerieNameFromQuery(row Row, column string) string
|
||||
return fmt.Sprintf("%s.%s", row.Name, column)
|
||||
}
|
||||
|
||||
func (rp *ResponseParser) parseTimepoint(valuePair []interface{}, valuePosition int) (tsdb.TimePoint, error) {
|
||||
func (rp *ResponseParser) parseTimepoint(valuePair []interface{}, valuePosition int) (monitor.TimePoint, error) {
|
||||
var value *float64 = rp.parseValue(valuePair[valuePosition])
|
||||
|
||||
timestampNumber, _ := valuePair[0].(json.Number)
|
||||
timestamp, err := timestampNumber.Float64()
|
||||
if err != nil {
|
||||
return tsdb.TimePoint{}, err
|
||||
return monitor.TimePoint{}, err
|
||||
}
|
||||
|
||||
return tsdb.NewTimePoint(value, timestamp), nil
|
||||
return monitor.NewTimePoint(value, timestamp), nil
|
||||
}
|
||||
|
||||
func (rp *ResponseParser) parseTimepointV2(valuePair []interface{}) (tsdb.TimePoint, error) {
|
||||
timepoint := make(tsdb.TimePoint, 0)
|
||||
func (rp *ResponseParser) parseTimepointV2(valuePair []interface{}) (monitor.TimePoint, error) {
|
||||
timepoint := make(monitor.TimePoint, 0)
|
||||
for i := 1; i < len(valuePair); i++ {
|
||||
timepoint = append(timepoint, rp.parseValueV2(valuePair[i]))
|
||||
}
|
||||
timestampNumber, _ := valuePair[0].(json.Number)
|
||||
timestamp, err := timestampNumber.Float64()
|
||||
if err != nil {
|
||||
return tsdb.TimePoint{}, errors.Wrapf(err, "timestampNumber.Float64 of %#v", timestampNumber)
|
||||
return monitor.TimePoint{}, errors.Wrapf(err, "timestampNumber.Float64 of %#v", timestampNumber)
|
||||
}
|
||||
timepoint = append(timepoint, timestamp)
|
||||
return timepoint, nil
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
@@ -149,16 +150,16 @@ func newPointsByResults(results []ResponseDataResult) ([]*points, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func transPointsToSeries(points []*points, query *tsdb.Query) tsdb.TimeSeriesSlice {
|
||||
var result tsdb.TimeSeriesSlice
|
||||
func transPointsToSeries(points []*points, query *tsdb.Query) monitor.TimeSeriesSlice {
|
||||
var result monitor.TimeSeriesSlice
|
||||
for _, point := range points {
|
||||
result = append(result, transPointToSeries(point, query)...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func transValuesToTSDBPoints(vals []ResponseDataResultValue) tsdb.TimeSeriesPoints {
|
||||
var points tsdb.TimeSeriesPoints
|
||||
func transValuesToTSDBPoints(vals []ResponseDataResultValue) monitor.TimeSeriesPoints {
|
||||
var points monitor.TimeSeriesPoints
|
||||
for _, val := range vals {
|
||||
point, err := parseTimepoint(val)
|
||||
if err != nil {
|
||||
@@ -179,8 +180,8 @@ func reviseTags(tags map[string]string) map[string]string {
|
||||
return ret
|
||||
}
|
||||
|
||||
func transPointToSeries(p *points, query *tsdb.Query) tsdb.TimeSeriesSlice {
|
||||
var result tsdb.TimeSeriesSlice
|
||||
func transPointToSeries(p *points, query *tsdb.Query) monitor.TimeSeriesSlice {
|
||||
var result monitor.TimeSeriesSlice
|
||||
|
||||
points := transValuesToTSDBPoints(p.values)
|
||||
tags := reviseTags(p.tags)
|
||||
|
||||
@@ -124,7 +124,7 @@ func convertVMResponse(rawQuery string, tsdbQuery *tsdb.TsdbQuery, resp *Respons
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "translate response")
|
||||
}
|
||||
ret.Meta = tsdb.QueryResultMeta{
|
||||
ret.Meta = monitor.QueryResultMeta{
|
||||
RawQuery: rawQuery,
|
||||
}
|
||||
result.Results[query.RefId] = ret
|
||||
@@ -160,8 +160,8 @@ func translateResponse(resp *Response, query *tsdb.Query) (*tsdb.QueryResult, er
|
||||
}
|
||||
|
||||
// Check VictoriaMetrics response at: https://docs.victoriametrics.com/keyConcepts.html#range-query
|
||||
func transformSeries(vmResult ResponseDataResult, query *tsdb.Query) tsdb.TimeSeriesSlice {
|
||||
var result tsdb.TimeSeriesSlice
|
||||
func transformSeries(vmResult ResponseDataResult, query *tsdb.Query) monitor.TimeSeriesSlice {
|
||||
var result monitor.TimeSeriesSlice
|
||||
metric := vmResult.Metric
|
||||
|
||||
points := transValuesToTSDBPoints(vmResult.Values)
|
||||
@@ -200,13 +200,13 @@ func formatRawName(idx int, name string, query *tsdb.Query, tags map[string]stri
|
||||
return tsdb.FormatRawName(idx, name, groupByTags, tags)
|
||||
}
|
||||
|
||||
func parseTimepoint(val ResponseDataResultValue) (tsdb.TimePoint, error) {
|
||||
timepoint := make(tsdb.TimePoint, 0)
|
||||
func parseTimepoint(val ResponseDataResultValue) (monitor.TimePoint, error) {
|
||||
timepoint := make(monitor.TimePoint, 0)
|
||||
// parse timestamp
|
||||
timestampNumber, _ := val[0].(json.Number)
|
||||
timestamp, err := timestampNumber.Float64()
|
||||
if err != nil {
|
||||
return tsdb.TimePoint{}, errors.Wrapf(err, "parse timestampNumber")
|
||||
return monitor.TimePoint{}, errors.Wrapf(err, "parse timestampNumber")
|
||||
}
|
||||
// to influxdb timestamp format, millisecond ?
|
||||
timestamp *= 1000
|
||||
|
||||
+11
-101
@@ -17,7 +17,6 @@ package tsdb
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
@@ -44,37 +43,24 @@ type Response struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type QueryResultMeta struct {
|
||||
RawQuery string `json:"raw_query"`
|
||||
}
|
||||
|
||||
type QueryResult struct {
|
||||
Error error `json:"-"`
|
||||
ErrorString string `json:"error,omitempty"`
|
||||
RefId string `json:"ref_id"`
|
||||
Meta QueryResultMeta `json:"meta"`
|
||||
Series TimeSeriesSlice `json:"series"`
|
||||
Tables []*Table `json:"tables"`
|
||||
Dataframes [][]byte `json:"dataframes"`
|
||||
}
|
||||
|
||||
type TimeSeries struct {
|
||||
// RawName is used to frontend displaying the curve name
|
||||
RawName string `json:"raw_name"`
|
||||
Columns []string `json:"columns"`
|
||||
Name string `json:"name"`
|
||||
Points TimeSeriesPoints `json:"points"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Error error `json:"-"`
|
||||
ErrorString string `json:"error,omitempty"`
|
||||
RefId string `json:"ref_id"`
|
||||
Meta api.QueryResultMeta `json:"meta"`
|
||||
Series api.TimeSeriesSlice `json:"series"`
|
||||
Tables []*Table `json:"tables"`
|
||||
Dataframes [][]byte `json:"dataframes"`
|
||||
}
|
||||
|
||||
func NewTimeSeries(
|
||||
name string,
|
||||
rawName string,
|
||||
columns []string,
|
||||
points TimeSeriesPoints,
|
||||
points api.TimeSeriesPoints,
|
||||
tags map[string]string,
|
||||
) *TimeSeries {
|
||||
return &TimeSeries{
|
||||
) *api.TimeSeries {
|
||||
return &api.TimeSeries{
|
||||
RawName: rawName,
|
||||
Columns: columns,
|
||||
Name: name,
|
||||
@@ -93,89 +79,13 @@ type TableColumn struct {
|
||||
}
|
||||
|
||||
type RowValues []interface{}
|
||||
type TimePoint []interface{}
|
||||
type TimeSeriesPoints []TimePoint
|
||||
type TimeSeriesSlice []*TimeSeries
|
||||
|
||||
func NewQueryResult() *QueryResult {
|
||||
return &QueryResult{
|
||||
Series: make(TimeSeriesSlice, 0),
|
||||
Series: make(api.TimeSeriesSlice, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimePoint(value *float64, timestamp float64) TimePoint {
|
||||
return TimePoint{value, timestamp}
|
||||
}
|
||||
|
||||
func NewTimePointByVal(value float64, timestamp float64) TimePoint {
|
||||
return NewTimePoint(&value, timestamp)
|
||||
}
|
||||
|
||||
func (p TimePoint) IsValid() bool {
|
||||
if val, ok := p[0].(*float64); ok && val != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
//return p[0].(*float64) != nil
|
||||
}
|
||||
|
||||
func (p TimePoint) IsValids() bool {
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
if p[i] == nil {
|
||||
return false
|
||||
}
|
||||
if p[i].(*float64) == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p TimePoint) Value() float64 {
|
||||
return *(p[0].(*float64))
|
||||
}
|
||||
|
||||
func (p TimePoint) Timestamp() float64 {
|
||||
return p[len(p)-1].(float64)
|
||||
}
|
||||
|
||||
func (p TimePoint) Values() []float64 {
|
||||
values := make([]float64, 0)
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
values = append(values, *(p[i].(*float64)))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func (p TimePoint) PointValueStr() []string {
|
||||
arrStr := make([]string, 0)
|
||||
for i := 0; i < len(p)-1; i++ {
|
||||
if p[i] == nil {
|
||||
arrStr = append(arrStr, "")
|
||||
}
|
||||
if fval, ok := p[i].(*float64); ok {
|
||||
arrStr = append(arrStr, strconv.FormatFloat((*fval), 'f', -1, 64))
|
||||
continue
|
||||
}
|
||||
if ival, ok := p[i].(*int64); ok {
|
||||
arrStr = append(arrStr, strconv.FormatInt((*ival), 64))
|
||||
continue
|
||||
}
|
||||
arrStr = append(arrStr, p[i].(string))
|
||||
}
|
||||
return arrStr
|
||||
}
|
||||
|
||||
func NewTimeSeriesPointsFromArgs(values ...float64) TimeSeriesPoints {
|
||||
points := make(TimeSeriesPoints, 0)
|
||||
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
points = append(points, NewTimePoint(&values[i], values[i+1]))
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
func FormatRawName(idx int, name string, groupByTags []string, tags map[string]string) string {
|
||||
// when group by tag specified
|
||||
if len(groupByTags) != 0 {
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewTimeRange(from, to string) *TimeRange {
|
||||
}
|
||||
}
|
||||
|
||||
func tryParseUnixMsEpoch(val string) (time.Time, bool) {
|
||||
func TryParseUnixMsEpoch(val string) (time.Time, bool) {
|
||||
if val, err := strconv.ParseInt(val, 10, 64); err == nil {
|
||||
seconds := val / 1000
|
||||
nano := (val - seconds*1000) * 1000000
|
||||
@@ -45,7 +45,8 @@ func tryParseUnixMsEpoch(val string) (time.Time, bool) {
|
||||
}
|
||||
|
||||
func (tr *TimeRange) ParseFrom() (time.Time, error) {
|
||||
if res, ok := tryParseUnixMsEpoch(tr.From); ok {
|
||||
res, ok := TryParseUnixMsEpoch(tr.From)
|
||||
if ok {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ func (tr *TimeRange) ParseTo() (time.Time, error) {
|
||||
return tr.now.Add(diff), nil
|
||||
}
|
||||
|
||||
if res, ok := tryParseUnixMsEpoch(tr.To); ok {
|
||||
if res, ok := TryParseUnixMsEpoch(tr.To); ok {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
merrors "yunion.io/x/onecloud/pkg/monitor/errors"
|
||||
"yunion.io/x/onecloud/pkg/monitor/tsdb"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -112,11 +113,11 @@ func ValidateAlertQueryModel(input monitor.MetricQuery) error {
|
||||
|
||||
func ValidateSelectOfMetricQuery(input monitor.AlertQuery) error {
|
||||
if err := ValidateFromAndToValue(input); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "ValidateFromAndToValue")
|
||||
}
|
||||
|
||||
if err := ValidateAlertQueryModel(input.Model); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "ValidateAlertQueryModel")
|
||||
}
|
||||
|
||||
for _, sel := range input.Model.Selects {
|
||||
@@ -128,28 +129,13 @@ func ValidateSelectOfMetricQuery(input monitor.AlertQuery) error {
|
||||
}
|
||||
|
||||
func ValidateFromAndToValue(input monitor.AlertQuery) error {
|
||||
fromRaw := strings.Replace(input.From, "now-", "", 1)
|
||||
|
||||
fromDur, err := time.ParseDuration("-" + fromRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := ValidateFromValue(input.From); err != nil {
|
||||
return errors.Wrap(err, "ValidateFromValue")
|
||||
}
|
||||
|
||||
if input.To == "now" {
|
||||
return nil
|
||||
} else if strings.HasPrefix(input.To, "now-") {
|
||||
withoutNow := strings.Replace(input.To, "now-", "", 1)
|
||||
|
||||
toDur, err := time.ParseDuration("-" + withoutNow)
|
||||
if err == nil {
|
||||
if toDur >= fromDur {
|
||||
return nil
|
||||
}
|
||||
return httperrors.NewInputParameterError("query duration err: from: %s, to:%s", input.From, input.To)
|
||||
}
|
||||
return err
|
||||
if err := ValidateToValue(input.To); err != nil {
|
||||
return errors.Wrap(err, "ValidateToValue")
|
||||
}
|
||||
return httperrors.NewInputParameterError("query duration `to` err: %s", input.To)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateAlertConditionReducer(input monitor.Condition) error {
|
||||
@@ -217,10 +203,18 @@ func HumanThresholdType(typ string) string {
|
||||
}
|
||||
|
||||
func ValidateFromValue(from string) error {
|
||||
fromRaw := strings.Replace(from, "now-", "", 1)
|
||||
_, ok := tsdb.TryParseUnixMsEpoch(from)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := time.ParseDuration("-" + fromRaw)
|
||||
return err
|
||||
fromRaw := strings.Replace(from, "now-", "", 1)
|
||||
fromRawStr := "-" + fromRaw
|
||||
_, err := time.ParseDuration(fromRawStr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "parse duration: %s", fromRawStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateToValue(to string) error {
|
||||
@@ -228,13 +222,16 @@ func ValidateToValue(to string) error {
|
||||
return nil
|
||||
} else if strings.HasPrefix(to, "now-") {
|
||||
withoutNow := strings.Replace(to, "now-", "", 1)
|
||||
|
||||
_, err := time.ParseDuration("-" + withoutNow)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := tsdb.TryParseUnixMsEpoch(to)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
_, err := time.ParseDuration(to)
|
||||
return err
|
||||
return errors.Wrapf(err, "parse to: %s", to)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user