@ -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" `
}