|
|
@@ -3,9 +3,17 @@ package influxdb
|
|
|
import (
|
|
|
"context"
|
|
|
"crypto/tls"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
"net/http"
|
|
|
+ "net/url"
|
|
|
+ "path"
|
|
|
"time"
|
|
|
|
|
|
+ "gopkg.in/guregu/null.v3"
|
|
|
+
|
|
|
+ "golang.org/x/net/context/ctxhttp"
|
|
|
+
|
|
|
"github.com/grafana/grafana/pkg/log"
|
|
|
"github.com/grafana/grafana/pkg/tsdb"
|
|
|
)
|
|
|
@@ -43,22 +51,141 @@ func init() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
|
|
|
- result := &tsdb.BatchResult{}
|
|
|
+func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (string, error) {
|
|
|
for _, v := range queries {
|
|
|
-
|
|
|
query, err := e.QueryParser.Parse(v.Model)
|
|
|
if err != nil {
|
|
|
- result.Error = err
|
|
|
- return result
|
|
|
+ return "", err
|
|
|
}
|
|
|
|
|
|
- glog.Info("Influxdb executor", "query", query)
|
|
|
+ rawQuery, err := e.QueryBuilder.Build(query, context)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return rawQuery, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return "", fmt.Errorf("Tsdb request contains no queries")
|
|
|
+}
|
|
|
+
|
|
|
+func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
|
|
|
+ result := &tsdb.BatchResult{}
|
|
|
+
|
|
|
+ query, err := e.getQuery(queries, context)
|
|
|
+ if err != nil {
|
|
|
+ result.Error = err
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ glog.Info("Influxdb", "query", query)
|
|
|
+
|
|
|
+ u, _ := url.Parse(e.Url)
|
|
|
+ u.Path = path.Join(u.Path, "query")
|
|
|
+
|
|
|
+ req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
|
+ if err != nil {
|
|
|
+ result.Error = err
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ params := req.URL.Query()
|
|
|
+ params.Set("q", query)
|
|
|
+ params.Set("db", e.Database)
|
|
|
+ params.Set("epoch", "s")
|
|
|
+
|
|
|
+ req.URL.RawQuery = params.Encode()
|
|
|
|
|
|
- rawQuery, err := e.QueryBuilder.Build(query)
|
|
|
+ req.Header.Set("Content-Type", "")
|
|
|
+ req.Header.Set("User-Agent", "Grafana")
|
|
|
+ if e.BasicAuth {
|
|
|
+ req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
|
|
|
+ }
|
|
|
+
|
|
|
+ glog.Info("influxdb request", "url", req.URL.String())
|
|
|
+ resp, err := ctxhttp.Do(ctx, HttpClient, req)
|
|
|
+ if err != nil {
|
|
|
+ result.Error = err
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.StatusCode/100 != 2 {
|
|
|
+ result.Error = fmt.Errorf("Influxdb returned statuscode %v body %v", resp.Status)
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ var response Response
|
|
|
+ dec := json.NewDecoder(resp.Body)
|
|
|
+ dec.UseNumber()
|
|
|
+ err = dec.Decode(&response)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error("Influxdb decode failed", "err", err)
|
|
|
+ result.Error = err
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ result.QueryResults = make(map[string]*tsdb.QueryResult)
|
|
|
+ queryRes := tsdb.NewQueryResult()
|
|
|
+
|
|
|
+ for _, v := range response.Results {
|
|
|
+ for _, r := range v.Series {
|
|
|
+ serie := tsdb.TimeSeries{Name: r.Name}
|
|
|
+ var points tsdb.TimeSeriesPoints
|
|
|
+
|
|
|
+ for _, k := range r.Values {
|
|
|
+ var value null.Float
|
|
|
+ var err error
|
|
|
+ num, ok := k[1].(json.Number)
|
|
|
+ if !ok {
|
|
|
+ value = null.FloatFromPtr(nil)
|
|
|
+ } else {
|
|
|
+ fvalue, err := num.Float64()
|
|
|
+ if err == nil {
|
|
|
+ value = null.FloatFrom(fvalue)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pos0, ok := k[0].(json.Number)
|
|
|
+ timestamp, err := pos0.Float64()
|
|
|
+ if err == nil && ok {
|
|
|
+ points = append(points, tsdb.NewTimePoint(value, timestamp))
|
|
|
+ } else {
|
|
|
+ glog.Error("Failed to convert response", "err1", err, "ok", ok, "timestamp", timestamp, "value", value.Float64)
|
|
|
+ }
|
|
|
+ serie.Points = points
|
|
|
+ }
|
|
|
+ queryRes.Series = append(queryRes.Series, &serie)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- glog.Info("Influxdb", "error", err, "rawQuery", rawQuery)
|
|
|
+ for _, v := range queryRes.Series {
|
|
|
+ glog.Info("result", "name", v.Name, "points", v.Points)
|
|
|
}
|
|
|
|
|
|
+ result.QueryResults["A"] = queryRes
|
|
|
+
|
|
|
return result
|
|
|
}
|
|
|
+
|
|
|
+type Response struct {
|
|
|
+ Results []Result
|
|
|
+ Err error
|
|
|
+}
|
|
|
+
|
|
|
+type Result struct {
|
|
|
+ Series []Row
|
|
|
+ Messages []*Message
|
|
|
+ Err error
|
|
|
+}
|
|
|
+
|
|
|
+type Message struct {
|
|
|
+ Level string `json:"level,omitempty"`
|
|
|
+ Text string `json:"text,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+type Row struct {
|
|
|
+ Name string `json:"name,omitempty"`
|
|
|
+ Tags map[string]string `json:"tags,omitempty"`
|
|
|
+ Columns []string `json:"columns,omitempty"`
|
|
|
+ Values [][]interface{} `json:"values,omitempty"`
|
|
|
+}
|