Pārlūkot izejas kodu

kill plugin processes when grafana shuts down

bergquist 8 gadi atpakaļ
vecāks
revīzija
e1d4bf2607

+ 5 - 6
pkg/cmd/grafana-server/server.go

@@ -22,7 +22,6 @@ import (
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
-	tsdbplugins "github.com/grafana/grafana/pkg/plugins/backend"
 	"github.com/grafana/grafana/pkg/services/alerting"
 	"github.com/grafana/grafana/pkg/services/alerting"
 	"github.com/grafana/grafana/pkg/services/cleanup"
 	"github.com/grafana/grafana/pkg/services/cleanup"
 	"github.com/grafana/grafana/pkg/services/notifications"
 	"github.com/grafana/grafana/pkg/services/notifications"
@@ -67,13 +66,13 @@ func (g *GrafanaServerImpl) Start() {
 	search.Init()
 	search.Init()
 	login.Init()
 	login.Init()
 	social.NewOAuthService()
 	social.NewOAuthService()
-	plugins.Init()
-	pluginClient, err := tsdbplugins.Init()
+
+	pluginCloser, err := plugins.Init()
 	if err != nil {
 	if err != nil {
 		g.log.Error("failed to start plugins", "error", err)
 		g.log.Error("failed to start plugins", "error", err)
 		g.Shutdown(1, "Startup failed")
 		g.Shutdown(1, "Startup failed")
 	}
 	}
-	defer pluginClient.Kill()
+	defer pluginCloser()
 
 
 	if err := provisioning.Init(g.context, setting.HomePath, setting.Cfg); err != nil {
 	if err := provisioning.Init(g.context, setting.HomePath, setting.Cfg); err != nil {
 		logger.Error("Failed to provision Grafana from config", "error", err)
 		logger.Error("Failed to provision Grafana from config", "error", err)
@@ -81,13 +80,13 @@ func (g *GrafanaServerImpl) Start() {
 		return
 		return
 	}
 	}
 
 
-	closer, err := tracing.Init(setting.Cfg)
+	tracingCloser, err := tracing.Init(setting.Cfg)
 	if err != nil {
 	if err != nil {
 		g.log.Error("Tracing settings is not valid", "error", err)
 		g.log.Error("Tracing settings is not valid", "error", err)
 		g.Shutdown(1, "Startup failed")
 		g.Shutdown(1, "Startup failed")
 		return
 		return
 	}
 	}
-	defer closer.Close()
+	defer tracingCloser.Close()
 
 
 	// init alerting
 	// init alerting
 	if setting.AlertingEnabled && setting.ExecuteAlerts {
 	if setting.AlertingEnabled && setting.ExecuteAlerts {

+ 20 - 20
pkg/plugins/backend/log-wrapper.go

@@ -1,4 +1,4 @@
-package plugins
+package backend
 
 
 import (
 import (
 	"log"
 	"log"
@@ -7,32 +7,32 @@ import (
 	hclog "github.com/hashicorp/go-hclog"
 	hclog "github.com/hashicorp/go-hclog"
 )
 )
 
 
-type logWrapper struct {
-	logger glog.Logger
+type LogWrapper struct {
+	Logger glog.Logger
 }
 }
 
 
-func (lw logWrapper) Trace(msg string, args ...interface{}) {}
-func (lw logWrapper) Debug(msg string, args ...interface{}) {}
-func (lw logWrapper) Info(msg string, args ...interface{})  {}
-func (lw logWrapper) Warn(msg string, args ...interface{})  {}
-func (lw logWrapper) Error(msg string, args ...interface{}) {}
+func (lw LogWrapper) Trace(msg string, args ...interface{}) {}
+func (lw LogWrapper) Debug(msg string, args ...interface{}) {}
+func (lw LogWrapper) Info(msg string, args ...interface{})  {}
+func (lw LogWrapper) Warn(msg string, args ...interface{})  {}
+func (lw LogWrapper) Error(msg string, args ...interface{}) {}
 
 
-func (lw logWrapper) IsTrace() bool { return true }
-func (lw logWrapper) IsDebug() bool { return true }
-func (lw logWrapper) IsInfo() bool  { return true }
-func (lw logWrapper) IsWarn() bool  { return true }
-func (lw logWrapper) IsError() bool { return true }
+func (lw LogWrapper) IsTrace() bool { return true }
+func (lw LogWrapper) IsDebug() bool { return true }
+func (lw LogWrapper) IsInfo() bool  { return true }
+func (lw LogWrapper) IsWarn() bool  { return true }
+func (lw LogWrapper) IsError() bool { return true }
 
 
-func (lw logWrapper) With(args ...interface{}) hclog.Logger {
-	return logWrapper{logger: glog.New("logger", args)}
+func (lw LogWrapper) With(args ...interface{}) hclog.Logger {
+	return LogWrapper{Logger: glog.New("logger", args)}
 }
 }
-func (lw logWrapper) Named(name string) hclog.Logger {
-	return logWrapper{logger: glog.New(name)}
+func (lw LogWrapper) Named(name string) hclog.Logger {
+	return LogWrapper{Logger: glog.New(name)}
 }
 }
-func (lw logWrapper) ResetNamed(name string) hclog.Logger {
-	return logWrapper{logger: glog.New(name)}
+func (lw LogWrapper) ResetNamed(name string) hclog.Logger {
+	return LogWrapper{Logger: glog.New(name)}
 }
 }
 
 
-func (lw logWrapper) StandardLogger(ops *hclog.StandardLoggerOptions) *log.Logger {
+func (lw LogWrapper) StandardLogger(ops *hclog.StandardLoggerOptions) *log.Logger {
 	return nil
 	return nil
 }
 }

+ 0 - 4
pkg/plugins/backend/mock_tsdb_plugin/Makefile

@@ -1,4 +0,0 @@
-all: build
-
-build: 
-	go build -o simple-plugin .

+ 0 - 39
pkg/plugins/backend/mock_tsdb_plugin/plugin.go

@@ -1,39 +0,0 @@
-package main
-
-import (
-	"golang.org/x/net/context"
-
-	"log"
-
-	shared "github.com/grafana/grafana/pkg/plugins/backend/shared"
-	proto "github.com/grafana/grafana/pkg/tsdb/models"
-	plugin "github.com/hashicorp/go-plugin"
-)
-
-type Tsdb struct {
-	plugin.NetRPCUnsupportedPlugin
-}
-
-func (Tsdb) Get(ctx context.Context, req *proto.TsdbRequest) (*proto.TsdbResponse, error) {
-	log.Print("Tsdb.Get() from plugin")
-
-	return &proto.TsdbResponse{
-		MetaJson: "from plugins! meta meta",
-	}, nil
-}
-
-func main() {
-	plugin.Serve(&plugin.ServeConfig{
-		HandshakeConfig: plugin.HandshakeConfig{
-			ProtocolVersion:  1,
-			MagicCookieKey:   "BASIC_PLUGIN",
-			MagicCookieValue: "hello",
-		},
-		Plugins: map[string]plugin.Plugin{
-			"tsdb_mock": &shared.TsdbPluginImpl{Plugin: &Tsdb{}},
-		},
-
-		// A non-nil value here enables gRPC serving for this plugin...
-		GRPCServer: plugin.DefaultGRPCServer,
-	})
-}

BIN
pkg/plugins/backend/mock_tsdb_plugin/simple-plugin


+ 0 - 55
pkg/plugins/backend/plugins.go

@@ -1,55 +0,0 @@
-package plugins
-
-import (
-	"os/exec"
-
-	"golang.org/x/net/context"
-
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/plugins/backend/shared"
-	"github.com/grafana/grafana/pkg/tsdb/models"
-	plugin "github.com/hashicorp/go-plugin"
-)
-
-func Init() (*plugin.Client, error) {
-	/*
-		setup protoc using https://gist.github.com/bergquist/5df1f201bb605e42538ef40f6ccf82a9
-		run "protoc --go_out=plugins=grpc:. *.proto" to update proto files
-	*/
-
-	logger := log.New("grafana.plugins")
-	client := plugin.NewClient(&plugin.ClientConfig{
-		HandshakeConfig: plugin.HandshakeConfig{
-			ProtocolVersion:  1,
-			MagicCookieKey:   "BASIC_PLUGIN",
-			MagicCookieValue: "hello",
-		},
-		Plugins:          shared.PluginMap,
-		Cmd:              exec.Command("sh", "-c", "/home/carl/go/src/github.com/grafana/grafana/pkg/plugins/backend/mock_tsdb_plugin/simple-plugin"),
-		AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
-		Logger:           logWrapper{logger: logger},
-	})
-
-	// Connect via RPC
-	rpcClient, err := client.Client()
-	if err != nil {
-		return nil, err
-	}
-
-	// Request the plugin
-	raw, err := rpcClient.Dispense("tsdb_mock")
-	if err != nil {
-		return nil, err
-	}
-
-	plugin := raw.(shared.TsdbPlugin)
-	response, err := plugin.Get(context.Background(), &proto.TsdbRequest{})
-
-	if err != nil {
-		logger.Error("Response from plugin. ", "response", response)
-	} else {
-		logger.Info("Response from plugin. ", "response", response)
-	}
-
-	return client, nil
-}

+ 0 - 22
pkg/plugins/backend/shared/grpc.go

@@ -1,22 +0,0 @@
-package shared
-
-import (
-	proto "github.com/grafana/grafana/pkg/tsdb/models"
-	"golang.org/x/net/context"
-)
-
-type GRPCClient struct {
-	proto.TsdbPluginClient
-}
-
-func (m *GRPCClient) Get(ctx context.Context, req *proto.TsdbRequest) (*proto.TsdbResponse, error) {
-	return m.TsdbPluginClient.Get(ctx, req)
-}
-
-type GRPCServer struct {
-	TsdbPlugin
-}
-
-func (m *GRPCServer) Get(ctx context.Context, req *proto.TsdbRequest) (*proto.TsdbResponse, error) {
-	return m.TsdbPlugin.Get(ctx, req)
-}

+ 90 - 0
pkg/plugins/backend_datasource.go

@@ -0,0 +1,90 @@
+package plugins
+
+import (
+	"context"
+	"encoding/json"
+	"os/exec"
+	"path"
+
+	"github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/plugins/backend"
+	"github.com/grafana/grafana/pkg/tsdb"
+
+	"github.com/grafana/grafana/pkg/log"
+	proto "github.com/grafana/grafana/pkg/tsdb/models"
+	shared "github.com/grafana/grafana/pkg/tsdb/models/proxy"
+	plugin "github.com/hashicorp/go-plugin"
+)
+
+type BackendDatasource struct {
+	*PluginBase
+
+	Executable string
+	client     *plugin.Client
+}
+
+type Killable interface {
+	Kill()
+}
+
+type NoopKiller struct{}
+
+func (nk NoopKiller) Kill() {}
+
+func (p *BackendDatasource) initBackendPlugin() (Killable, error) {
+	logger := log.New("grafana.plugins")
+
+	p.client = plugin.NewClient(&plugin.ClientConfig{
+		HandshakeConfig: plugin.HandshakeConfig{
+			ProtocolVersion:  1,
+			MagicCookieKey:   "BASIC_PLUGIN",
+			MagicCookieValue: "hello",
+		},
+		Plugins:          map[string]plugin.Plugin{p.Id: &shared.TsdbPluginImpl{}},
+		Cmd:              exec.Command("sh", "-c", path.Join(p.PluginDir, p.Executable)),
+		AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
+		Logger:           backend.LogWrapper{Logger: logger},
+	})
+
+	rpcClient, err := p.client.Client()
+	if err != nil {
+		return NoopKiller{}, err
+	}
+
+	raw, err := rpcClient.Dispense(p.Id)
+	if err != nil {
+		return NoopKiller{}, err
+	}
+
+	plugin := raw.(shared.TsdbPlugin)
+	response, err := plugin.Query(context.Background(), &proto.TsdbQuery{})
+
+	if err != nil {
+		logger.Error("Response from plugin. ", "response", response)
+	} else {
+		logger.Info("Response from plugin. ", "response", response)
+	}
+
+	tsdb.RegisterTsdbQueryEndpoint(p.Id, func(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
+		return &shared.TsdbWrapper{TsdbPlugin: plugin}, nil
+	})
+
+	return p.client, nil
+}
+
+func (p *BackendDatasource) Kill() {
+	p.client.Kill()
+}
+
+func (p *BackendDatasource) Load(decoder *json.Decoder, pluginDir string) error {
+	if err := decoder.Decode(&p); err != nil {
+		return err
+	}
+
+	if err := p.registerPlugin(pluginDir); err != nil {
+		return err
+	}
+
+	BackendDatasources[p.Id] = p
+	return nil
+}

+ 32 - 11
pkg/plugins/plugins.go

@@ -17,12 +17,13 @@ import (
 )
 )
 
 
 var (
 var (
-	DataSources  map[string]*DataSourcePlugin
-	Panels       map[string]*PanelPlugin
-	StaticRoutes []*PluginStaticRoute
-	Apps         map[string]*AppPlugin
-	Plugins      map[string]*PluginBase
-	PluginTypes  map[string]interface{}
+	DataSources        map[string]*DataSourcePlugin
+	Panels             map[string]*PanelPlugin
+	StaticRoutes       []*PluginStaticRoute
+	Apps               map[string]*AppPlugin
+	Plugins            map[string]*PluginBase
+	BackendDatasources map[string]*BackendDatasource
+	PluginTypes        map[string]interface{}
 
 
 	GrafanaLatestVersion string
 	GrafanaLatestVersion string
 	GrafanaHasUpdate     bool
 	GrafanaHasUpdate     bool
@@ -34,7 +35,9 @@ type PluginScanner struct {
 	errors     []error
 	errors     []error
 }
 }
 
 
-func Init() error {
+type Dispose func()
+
+func Init() (Dispose, error) {
 	plog = log.New("plugins")
 	plog = log.New("plugins")
 
 
 	DataSources = make(map[string]*DataSourcePlugin)
 	DataSources = make(map[string]*DataSourcePlugin)
@@ -42,10 +45,12 @@ func Init() error {
 	Panels = make(map[string]*PanelPlugin)
 	Panels = make(map[string]*PanelPlugin)
 	Apps = make(map[string]*AppPlugin)
 	Apps = make(map[string]*AppPlugin)
 	Plugins = make(map[string]*PluginBase)
 	Plugins = make(map[string]*PluginBase)
+	BackendDatasources = make(map[string]*BackendDatasource)
 	PluginTypes = map[string]interface{}{
 	PluginTypes = map[string]interface{}{
-		"panel":      PanelPlugin{},
-		"datasource": DataSourcePlugin{},
-		"app":        AppPlugin{},
+		"panel":              PanelPlugin{},
+		"datasource":         DataSourcePlugin{},
+		"app":                AppPlugin{},
+		"backend-datasource": BackendDatasource{},
 	}
 	}
 
 
 	plog.Info("Starting plugin search")
 	plog.Info("Starting plugin search")
@@ -77,10 +82,26 @@ func Init() error {
 		app.initApp()
 		app.initApp()
 	}
 	}
 
 
+	killers := []Killable{}
+	for _, be := range BackendDatasources {
+		killable, err := be.initBackendPlugin()
+		if err != nil {
+			plog.Error("failed to init plugin", "id", be.Id, "error", err)
+		} else {
+			killers = append(killers, killable)
+		}
+	}
+
 	go StartPluginUpdateChecker()
 	go StartPluginUpdateChecker()
 	go updateAppDashboards()
 	go updateAppDashboards()
 
 
-	return nil
+	return dispose, nil
+}
+
+func dispose() {
+	for _, p := range BackendDatasources {
+		p.Kill()
+	}
 }
 }
 
 
 func checkPluginPaths() error {
 func checkPluginPaths() error {

+ 0 - 2
pkg/tsdb/models.go

@@ -14,9 +14,7 @@ type TsdbQuery struct {
 type Query struct {
 type Query struct {
 	RefId         string
 	RefId         string
 	Model         *simplejson.Json
 	Model         *simplejson.Json
-	Depends       []string
 	DataSource    *models.DataSource
 	DataSource    *models.DataSource
-	Results       []*TimeSeries
 	MaxDataPoints int64
 	MaxDataPoints int64
 	IntervalMs    int64
 	IntervalMs    int64
 }
 }

+ 24 - 0
pkg/tsdb/models/proxy/grpc.go

@@ -0,0 +1,24 @@
+package proxy
+
+import (
+	proto "github.com/grafana/grafana/pkg/tsdb/models"
+	"golang.org/x/net/context"
+)
+
+type GRPCClient struct {
+	proto.TsdbPluginClient
+}
+
+//Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) (*Response, error)
+
+func (m *GRPCClient) Query(ctx context.Context, req *proto.TsdbQuery) (*proto.Response, error) {
+	return m.TsdbPluginClient.Query(ctx, req)
+}
+
+type GRPCServer struct {
+	TsdbPlugin
+}
+
+func (m *GRPCServer) Query(ctx context.Context, req *proto.TsdbQuery) (*proto.Response, error) {
+	return m.TsdbPlugin.Query(ctx, req)
+}

+ 4 - 2
pkg/plugins/backend/shared/interface.go → pkg/tsdb/models/proxy/interface.go

@@ -1,8 +1,10 @@
-package shared
+package proxy
 
 
 import (
 import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	//tsdb "github.com/grafana/grafana/pkg/tsdb"
+
 	proto "github.com/grafana/grafana/pkg/tsdb/models"
 	proto "github.com/grafana/grafana/pkg/tsdb/models"
 	plugin "github.com/hashicorp/go-plugin"
 	plugin "github.com/hashicorp/go-plugin"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc"
@@ -13,7 +15,7 @@ var PluginMap = map[string]plugin.Plugin{
 }
 }
 
 
 type TsdbPlugin interface {
 type TsdbPlugin interface {
-	Get(ctx context.Context, req *proto.TsdbRequest) (*proto.TsdbResponse, error)
+	Query(ctx context.Context, req *proto.TsdbQuery) (*proto.Response, error)
 }
 }
 
 
 type TsdbPluginImpl struct { //LOL IMPL LOL
 type TsdbPluginImpl struct { //LOL IMPL LOL

+ 83 - 0
pkg/tsdb/models/proxy/wrapper.go

@@ -0,0 +1,83 @@
+package proxy
+
+import (
+	"github.com/golang/protobuf/ptypes"
+	"github.com/grafana/grafana/pkg/components/null"
+	"github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/tsdb"
+	proto "github.com/grafana/grafana/pkg/tsdb/models"
+	"golang.org/x/net/context"
+)
+
+type TsdbWrapper struct {
+	TsdbPlugin
+}
+
+func (tw *TsdbWrapper) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	jsonData, _ := ds.JsonData.MarshalJSON()
+	now, _ := ptypes.TimestampProto(query.TimeRange.Now)
+
+	pbQuery := &proto.TsdbQuery{
+		Datasource: &proto.DatasourceInfo{
+			Access:            string(ds.Access),
+			BasicAuth:         ds.BasicAuth,
+			BasicAuthUser:     ds.BasicAuthUser,
+			BasicAuthPassword: ds.BasicAuthPassword,
+			JsonData:          string(jsonData),
+			Name:              ds.Name,
+			Type:              ds.Type,
+			Url:               ds.Url,
+		},
+		Timerange: &proto.Timerange{
+			From: query.TimeRange.From,
+			To:   query.TimeRange.To,
+			Now:  now,
+		},
+		Queries: []*proto.Query{},
+	}
+
+	for _, q := range query.Queries {
+		modelJson, _ := q.Model.MarshalJSON()
+
+		pbQuery.Queries = append(pbQuery.Queries, &proto.Query{
+			ModelJson:     string(modelJson),
+			IntervalMs:    q.IntervalMs,
+			RefId:         q.RefId,
+			MaxDataPoints: q.MaxDataPoints,
+		})
+	}
+
+	pbres, err := tw.TsdbPlugin.Query(ctx, pbQuery)
+
+	if err != nil {
+		return nil, err
+	}
+
+	res := &tsdb.Response{
+		Message: pbres.Message,
+	}
+
+	for _, r := range pbres.Results {
+		res.Results[r.RefId] = &tsdb.QueryResult{
+			RefId:  r.RefId,
+			Series: []*tsdb.TimeSeries{},
+		}
+
+		for _, s := range r.Series {
+			points := tsdb.TimeSeriesPoints{}
+
+			for _, p := range s.Points {
+				po := tsdb.NewTimePoint(null.FloatFrom(float64(p.Timestamp.Nanos)), p.Value)
+				points = append(points, po)
+			}
+
+			res.Results[r.RefId].Series = append(res.Results[r.RefId].Series, &tsdb.TimeSeries{
+				Name:   s.Name,
+				Tags:   s.Tags,
+				Points: points,
+			})
+		}
+	}
+
+	return res, nil
+}

+ 309 - 69
pkg/tsdb/models/tsdb_plugin.pb.go

@@ -8,16 +8,21 @@ It is generated from these files:
 	tsdb_plugin.proto
 	tsdb_plugin.proto
 
 
 It has these top-level messages:
 It has these top-level messages:
-	TsdbRequest
 	TsdbQuery
 	TsdbQuery
-	TsdbResponse
+	Query
+	Timerange
+	Response
+	QueryResult
+	DatasourceInfo
 	TsdbSeries
 	TsdbSeries
+	Point
 */
 */
 package proto
 package proto
 
 
 import proto1 "github.com/golang/protobuf/proto"
 import proto1 "github.com/golang/protobuf/proto"
 import fmt "fmt"
 import fmt "fmt"
 import math "math"
 import math "math"
+import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
 
 
 import (
 import (
 	context "golang.org/x/net/context"
 	context "golang.org/x/net/context"
@@ -36,55 +41,143 @@ var _ = math.Inf
 const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
 const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
 
 
 // Message represents a simple message sent to the Echo service.
 // Message represents a simple message sent to the Echo service.
-type TsdbRequest struct {
-	From    int64        `protobuf:"varint,1,opt,name=from" json:"from,omitempty"`
-	To      int64        `protobuf:"varint,2,opt,name=to" json:"to,omitempty"`
-	Queries []*TsdbQuery `protobuf:"bytes,3,rep,name=queries" json:"queries,omitempty"`
+type TsdbQuery struct {
+	Timerange  *Timerange      `protobuf:"bytes,1,opt,name=timerange" json:"timerange,omitempty"`
+	Datasource *DatasourceInfo `protobuf:"bytes,2,opt,name=datasource" json:"datasource,omitempty"`
+	Queries    []*Query        `protobuf:"bytes,3,rep,name=queries" json:"queries,omitempty"`
 }
 }
 
 
-func (m *TsdbRequest) Reset()                    { *m = TsdbRequest{} }
-func (m *TsdbRequest) String() string            { return proto1.CompactTextString(m) }
-func (*TsdbRequest) ProtoMessage()               {}
-func (*TsdbRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *TsdbQuery) Reset()                    { *m = TsdbQuery{} }
+func (m *TsdbQuery) String() string            { return proto1.CompactTextString(m) }
+func (*TsdbQuery) ProtoMessage()               {}
+func (*TsdbQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
 
 
-func (m *TsdbRequest) GetFrom() int64 {
+func (m *TsdbQuery) GetTimerange() *Timerange {
 	if m != nil {
 	if m != nil {
-		return m.From
+		return m.Timerange
+	}
+	return nil
+}
+
+func (m *TsdbQuery) GetDatasource() *DatasourceInfo {
+	if m != nil {
+		return m.Datasource
+	}
+	return nil
+}
+
+func (m *TsdbQuery) GetQueries() []*Query {
+	if m != nil {
+		return m.Queries
+	}
+	return nil
+}
+
+type Query struct {
+	RefId         string          `protobuf:"bytes,1,opt,name=refId" json:"refId,omitempty"`
+	ModelJson     string          `protobuf:"bytes,2,opt,name=modelJson" json:"modelJson,omitempty"`
+	MaxDataPoints int64           `protobuf:"varint,3,opt,name=MaxDataPoints" json:"MaxDataPoints,omitempty"`
+	IntervalMs    int64           `protobuf:"varint,4,opt,name=intervalMs" json:"intervalMs,omitempty"`
+	Datasource    *DatasourceInfo `protobuf:"bytes,5,opt,name=datasource" json:"datasource,omitempty"`
+}
+
+func (m *Query) Reset()                    { *m = Query{} }
+func (m *Query) String() string            { return proto1.CompactTextString(m) }
+func (*Query) ProtoMessage()               {}
+func (*Query) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Query) GetRefId() string {
+	if m != nil {
+		return m.RefId
+	}
+	return ""
+}
+
+func (m *Query) GetModelJson() string {
+	if m != nil {
+		return m.ModelJson
+	}
+	return ""
+}
+
+func (m *Query) GetMaxDataPoints() int64 {
+	if m != nil {
+		return m.MaxDataPoints
 	}
 	}
 	return 0
 	return 0
 }
 }
 
 
-func (m *TsdbRequest) GetTo() int64 {
+func (m *Query) GetIntervalMs() int64 {
 	if m != nil {
 	if m != nil {
-		return m.To
+		return m.IntervalMs
 	}
 	}
 	return 0
 	return 0
 }
 }
 
 
-func (m *TsdbRequest) GetQueries() []*TsdbQuery {
+func (m *Query) GetDatasource() *DatasourceInfo {
 	if m != nil {
 	if m != nil {
-		return m.Queries
+		return m.Datasource
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-type TsdbQuery struct {
-	QueryJson string `protobuf:"bytes,1,opt,name=queryJson" json:"queryJson,omitempty"`
+type Timerange struct {
+	From string                     `protobuf:"bytes,1,opt,name=from" json:"from,omitempty"`
+	To   string                     `protobuf:"bytes,2,opt,name=to" json:"to,omitempty"`
+	Now  *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=now" json:"now,omitempty"`
 }
 }
 
 
-func (m *TsdbQuery) Reset()                    { *m = TsdbQuery{} }
-func (m *TsdbQuery) String() string            { return proto1.CompactTextString(m) }
-func (*TsdbQuery) ProtoMessage()               {}
-func (*TsdbQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *Timerange) Reset()                    { *m = Timerange{} }
+func (m *Timerange) String() string            { return proto1.CompactTextString(m) }
+func (*Timerange) ProtoMessage()               {}
+func (*Timerange) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *Timerange) GetFrom() string {
+	if m != nil {
+		return m.From
+	}
+	return ""
+}
+
+func (m *Timerange) GetTo() string {
+	if m != nil {
+		return m.To
+	}
+	return ""
+}
+
+func (m *Timerange) GetNow() *google_protobuf.Timestamp {
+	if m != nil {
+		return m.Now
+	}
+	return nil
+}
+
+type Response struct {
+	Message string         `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
+	Results []*QueryResult `protobuf:"bytes,2,rep,name=results" json:"results,omitempty"`
+}
+
+func (m *Response) Reset()                    { *m = Response{} }
+func (m *Response) String() string            { return proto1.CompactTextString(m) }
+func (*Response) ProtoMessage()               {}
+func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
 
 
-func (m *TsdbQuery) GetQueryJson() string {
+func (m *Response) GetMessage() string {
 	if m != nil {
 	if m != nil {
-		return m.QueryJson
+		return m.Message
 	}
 	}
 	return ""
 	return ""
 }
 }
 
 
-type TsdbResponse struct {
+func (m *Response) GetResults() []*QueryResult {
+	if m != nil {
+		return m.Results
+	}
+	return nil
+}
+
+type QueryResult struct {
 	Error       string        `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
 	Error       string        `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
 	ErrorString string        `protobuf:"bytes,2,opt,name=errorString" json:"errorString,omitempty"`
 	ErrorString string        `protobuf:"bytes,2,opt,name=errorString" json:"errorString,omitempty"`
 	RefId       string        `protobuf:"bytes,3,opt,name=refId" json:"refId,omitempty"`
 	RefId       string        `protobuf:"bytes,3,opt,name=refId" json:"refId,omitempty"`
@@ -92,54 +185,136 @@ type TsdbResponse struct {
 	Series      []*TsdbSeries `protobuf:"bytes,5,rep,name=series" json:"series,omitempty"`
 	Series      []*TsdbSeries `protobuf:"bytes,5,rep,name=series" json:"series,omitempty"`
 }
 }
 
 
-func (m *TsdbResponse) Reset()                    { *m = TsdbResponse{} }
-func (m *TsdbResponse) String() string            { return proto1.CompactTextString(m) }
-func (*TsdbResponse) ProtoMessage()               {}
-func (*TsdbResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *QueryResult) Reset()                    { *m = QueryResult{} }
+func (m *QueryResult) String() string            { return proto1.CompactTextString(m) }
+func (*QueryResult) ProtoMessage()               {}
+func (*QueryResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
 
 
-func (m *TsdbResponse) GetError() string {
+func (m *QueryResult) GetError() string {
 	if m != nil {
 	if m != nil {
 		return m.Error
 		return m.Error
 	}
 	}
 	return ""
 	return ""
 }
 }
 
 
-func (m *TsdbResponse) GetErrorString() string {
+func (m *QueryResult) GetErrorString() string {
 	if m != nil {
 	if m != nil {
 		return m.ErrorString
 		return m.ErrorString
 	}
 	}
 	return ""
 	return ""
 }
 }
 
 
-func (m *TsdbResponse) GetRefId() string {
+func (m *QueryResult) GetRefId() string {
 	if m != nil {
 	if m != nil {
 		return m.RefId
 		return m.RefId
 	}
 	}
 	return ""
 	return ""
 }
 }
 
 
-func (m *TsdbResponse) GetMetaJson() string {
+func (m *QueryResult) GetMetaJson() string {
 	if m != nil {
 	if m != nil {
 		return m.MetaJson
 		return m.MetaJson
 	}
 	}
 	return ""
 	return ""
 }
 }
 
 
-func (m *TsdbResponse) GetSeries() []*TsdbSeries {
+func (m *QueryResult) GetSeries() []*TsdbSeries {
 	if m != nil {
 	if m != nil {
 		return m.Series
 		return m.Series
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
+type DatasourceInfo struct {
+	Name              string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	Type              string `protobuf:"bytes,2,opt,name=type" json:"type,omitempty"`
+	Access            string `protobuf:"bytes,3,opt,name=access" json:"access,omitempty"`
+	Url               string `protobuf:"bytes,4,opt,name=url" json:"url,omitempty"`
+	BasicAuth         bool   `protobuf:"varint,5,opt,name=basicAuth" json:"basicAuth,omitempty"`
+	BasicAuthUser     string `protobuf:"bytes,6,opt,name=basicAuthUser" json:"basicAuthUser,omitempty"`
+	BasicAuthPassword string `protobuf:"bytes,7,opt,name=basicAuthPassword" json:"basicAuthPassword,omitempty"`
+	JsonData          string `protobuf:"bytes,8,opt,name=jsonData" json:"jsonData,omitempty"`
+	SecureJsonData    string `protobuf:"bytes,9,opt,name=secureJsonData" json:"secureJsonData,omitempty"`
+}
+
+func (m *DatasourceInfo) Reset()                    { *m = DatasourceInfo{} }
+func (m *DatasourceInfo) String() string            { return proto1.CompactTextString(m) }
+func (*DatasourceInfo) ProtoMessage()               {}
+func (*DatasourceInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+
+func (m *DatasourceInfo) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetType() string {
+	if m != nil {
+		return m.Type
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetAccess() string {
+	if m != nil {
+		return m.Access
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetUrl() string {
+	if m != nil {
+		return m.Url
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetBasicAuth() bool {
+	if m != nil {
+		return m.BasicAuth
+	}
+	return false
+}
+
+func (m *DatasourceInfo) GetBasicAuthUser() string {
+	if m != nil {
+		return m.BasicAuthUser
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetBasicAuthPassword() string {
+	if m != nil {
+		return m.BasicAuthPassword
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetJsonData() string {
+	if m != nil {
+		return m.JsonData
+	}
+	return ""
+}
+
+func (m *DatasourceInfo) GetSecureJsonData() string {
+	if m != nil {
+		return m.SecureJsonData
+	}
+	return ""
+}
+
 type TsdbSeries struct {
 type TsdbSeries struct {
-	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	Name   string            `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	Tags   map[string]string `protobuf:"bytes,2,rep,name=tags" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	Points []*Point          `protobuf:"bytes,3,rep,name=points" json:"points,omitempty"`
 }
 }
 
 
 func (m *TsdbSeries) Reset()                    { *m = TsdbSeries{} }
 func (m *TsdbSeries) Reset()                    { *m = TsdbSeries{} }
 func (m *TsdbSeries) String() string            { return proto1.CompactTextString(m) }
 func (m *TsdbSeries) String() string            { return proto1.CompactTextString(m) }
 func (*TsdbSeries) ProtoMessage()               {}
 func (*TsdbSeries) ProtoMessage()               {}
-func (*TsdbSeries) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (*TsdbSeries) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
 
 
 func (m *TsdbSeries) GetName() string {
 func (m *TsdbSeries) GetName() string {
 	if m != nil {
 	if m != nil {
@@ -148,11 +323,53 @@ func (m *TsdbSeries) GetName() string {
 	return ""
 	return ""
 }
 }
 
 
+func (m *TsdbSeries) GetTags() map[string]string {
+	if m != nil {
+		return m.Tags
+	}
+	return nil
+}
+
+func (m *TsdbSeries) GetPoints() []*Point {
+	if m != nil {
+		return m.Points
+	}
+	return nil
+}
+
+type Point struct {
+	Timestamp *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=timestamp" json:"timestamp,omitempty"`
+	Value     float64                    `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
+}
+
+func (m *Point) Reset()                    { *m = Point{} }
+func (m *Point) String() string            { return proto1.CompactTextString(m) }
+func (*Point) ProtoMessage()               {}
+func (*Point) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+
+func (m *Point) GetTimestamp() *google_protobuf.Timestamp {
+	if m != nil {
+		return m.Timestamp
+	}
+	return nil
+}
+
+func (m *Point) GetValue() float64 {
+	if m != nil {
+		return m.Value
+	}
+	return 0
+}
+
 func init() {
 func init() {
-	proto1.RegisterType((*TsdbRequest)(nil), "plugins.TsdbRequest")
 	proto1.RegisterType((*TsdbQuery)(nil), "plugins.TsdbQuery")
 	proto1.RegisterType((*TsdbQuery)(nil), "plugins.TsdbQuery")
-	proto1.RegisterType((*TsdbResponse)(nil), "plugins.TsdbResponse")
+	proto1.RegisterType((*Query)(nil), "plugins.Query")
+	proto1.RegisterType((*Timerange)(nil), "plugins.Timerange")
+	proto1.RegisterType((*Response)(nil), "plugins.Response")
+	proto1.RegisterType((*QueryResult)(nil), "plugins.QueryResult")
+	proto1.RegisterType((*DatasourceInfo)(nil), "plugins.DatasourceInfo")
 	proto1.RegisterType((*TsdbSeries)(nil), "plugins.TsdbSeries")
 	proto1.RegisterType((*TsdbSeries)(nil), "plugins.TsdbSeries")
+	proto1.RegisterType((*Point)(nil), "plugins.Point")
 }
 }
 
 
 // Reference imports to suppress errors if they are not otherwise used.
 // Reference imports to suppress errors if they are not otherwise used.
@@ -166,7 +383,7 @@ const _ = grpc.SupportPackageIsVersion4
 // Client API for TsdbPlugin service
 // Client API for TsdbPlugin service
 
 
 type TsdbPluginClient interface {
 type TsdbPluginClient interface {
-	Get(ctx context.Context, in *TsdbRequest, opts ...grpc.CallOption) (*TsdbResponse, error)
+	Query(ctx context.Context, in *TsdbQuery, opts ...grpc.CallOption) (*Response, error)
 }
 }
 
 
 type tsdbPluginClient struct {
 type tsdbPluginClient struct {
@@ -177,9 +394,9 @@ func NewTsdbPluginClient(cc *grpc.ClientConn) TsdbPluginClient {
 	return &tsdbPluginClient{cc}
 	return &tsdbPluginClient{cc}
 }
 }
 
 
-func (c *tsdbPluginClient) Get(ctx context.Context, in *TsdbRequest, opts ...grpc.CallOption) (*TsdbResponse, error) {
-	out := new(TsdbResponse)
-	err := grpc.Invoke(ctx, "/plugins.TsdbPlugin/Get", in, out, c.cc, opts...)
+func (c *tsdbPluginClient) Query(ctx context.Context, in *TsdbQuery, opts ...grpc.CallOption) (*Response, error) {
+	out := new(Response)
+	err := grpc.Invoke(ctx, "/plugins.TsdbPlugin/Query", in, out, c.cc, opts...)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -189,27 +406,27 @@ func (c *tsdbPluginClient) Get(ctx context.Context, in *TsdbRequest, opts ...grp
 // Server API for TsdbPlugin service
 // Server API for TsdbPlugin service
 
 
 type TsdbPluginServer interface {
 type TsdbPluginServer interface {
-	Get(context.Context, *TsdbRequest) (*TsdbResponse, error)
+	Query(context.Context, *TsdbQuery) (*Response, error)
 }
 }
 
 
 func RegisterTsdbPluginServer(s *grpc.Server, srv TsdbPluginServer) {
 func RegisterTsdbPluginServer(s *grpc.Server, srv TsdbPluginServer) {
 	s.RegisterService(&_TsdbPlugin_serviceDesc, srv)
 	s.RegisterService(&_TsdbPlugin_serviceDesc, srv)
 }
 }
 
 
-func _TsdbPlugin_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(TsdbRequest)
+func _TsdbPlugin_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(TsdbQuery)
 	if err := dec(in); err != nil {
 	if err := dec(in); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	if interceptor == nil {
 	if interceptor == nil {
-		return srv.(TsdbPluginServer).Get(ctx, in)
+		return srv.(TsdbPluginServer).Query(ctx, in)
 	}
 	}
 	info := &grpc.UnaryServerInfo{
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
 		Server:     srv,
-		FullMethod: "/plugins.TsdbPlugin/Get",
+		FullMethod: "/plugins.TsdbPlugin/Query",
 	}
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(TsdbPluginServer).Get(ctx, req.(*TsdbRequest))
+		return srv.(TsdbPluginServer).Query(ctx, req.(*TsdbQuery))
 	}
 	}
 	return interceptor(ctx, in, info, handler)
 	return interceptor(ctx, in, info, handler)
 }
 }
@@ -219,8 +436,8 @@ var _TsdbPlugin_serviceDesc = grpc.ServiceDesc{
 	HandlerType: (*TsdbPluginServer)(nil),
 	HandlerType: (*TsdbPluginServer)(nil),
 	Methods: []grpc.MethodDesc{
 	Methods: []grpc.MethodDesc{
 		{
 		{
-			MethodName: "Get",
-			Handler:    _TsdbPlugin_Get_Handler,
+			MethodName: "Query",
+			Handler:    _TsdbPlugin_Query_Handler,
 		},
 		},
 	},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Streams:  []grpc.StreamDesc{},
@@ -230,23 +447,46 @@ var _TsdbPlugin_serviceDesc = grpc.ServiceDesc{
 func init() { proto1.RegisterFile("tsdb_plugin.proto", fileDescriptor0) }
 func init() { proto1.RegisterFile("tsdb_plugin.proto", fileDescriptor0) }
 
 
 var fileDescriptor0 = []byte{
 var fileDescriptor0 = []byte{
-	// 280 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x51, 0xc1, 0x4e, 0x83, 0x40,
-	0x10, 0x0d, 0x50, 0x8a, 0x0c, 0xc6, 0xc4, 0xb1, 0x26, 0xa4, 0xf1, 0x40, 0x38, 0xd5, 0x68, 0x38,
-	0xe0, 0x0f, 0x18, 0x2f, 0x46, 0x4f, 0xba, 0xf5, 0xe4, 0xa5, 0x29, 0x61, 0xda, 0x90, 0x08, 0x4b,
-	0x77, 0x97, 0x43, 0x3f, 0xc8, 0xff, 0x34, 0xcc, 0xae, 0xad, 0xf5, 0xc4, 0xbc, 0xb7, 0x6f, 0xde,
-	0xcc, 0x1b, 0xe0, 0xd2, 0xe8, 0xba, 0x5a, 0xf5, 0x5f, 0xc3, 0xb6, 0xe9, 0x8a, 0x5e, 0x49, 0x23,
-	0x31, 0xb2, 0x48, 0xe7, 0x2b, 0x48, 0x3e, 0x74, 0x5d, 0x09, 0xda, 0x0d, 0xa4, 0x0d, 0x22, 0x4c,
-	0x36, 0x4a, 0xb6, 0xa9, 0x97, 0x79, 0x8b, 0x40, 0x70, 0x8d, 0x17, 0xe0, 0x1b, 0x99, 0xfa, 0xcc,
-	0xf8, 0x46, 0xe2, 0x3d, 0x44, 0xbb, 0x81, 0x54, 0x43, 0x3a, 0x0d, 0xb2, 0x60, 0x91, 0x94, 0x58,
-	0x38, 0xb7, 0x62, 0xb4, 0x7a, 0x1f, 0x48, 0xed, 0xc5, 0xaf, 0x24, 0xbf, 0x85, 0xf8, 0xc0, 0xe2,
-	0x0d, 0xc4, 0x23, 0xbf, 0x7f, 0xd5, 0xb2, 0xe3, 0x19, 0xb1, 0x38, 0x12, 0xf9, 0xb7, 0x07, 0xe7,
-	0x76, 0x19, 0xdd, 0xcb, 0x4e, 0x13, 0xce, 0x20, 0x24, 0xa5, 0xa4, 0x72, 0x52, 0x0b, 0x30, 0x83,
-	0x84, 0x8b, 0xa5, 0x51, 0x4d, 0xb7, 0xe5, 0xc5, 0x62, 0xf1, 0x97, 0x1a, 0xfb, 0x14, 0x6d, 0x5e,
-	0xea, 0x34, 0xb0, 0x7d, 0x0c, 0x70, 0x0e, 0x67, 0x2d, 0x99, 0x35, 0xcf, 0x9e, 0xf0, 0xc3, 0x01,
-	0xe3, 0x1d, 0x4c, 0xb5, 0x8d, 0x14, 0x72, 0xa4, 0xab, 0x93, 0x48, 0x4b, 0x7e, 0x12, 0x4e, 0x92,
-	0x67, 0x00, 0x47, 0x76, 0x3c, 0x59, 0xb7, 0x6e, 0xc9, 0xed, 0xc8, 0x75, 0xf9, 0x68, 0x15, 0x6f,
-	0xec, 0x81, 0x25, 0x04, 0xcf, 0x64, 0x70, 0x76, 0xe2, 0xe9, 0x2e, 0x3e, 0xbf, 0xfe, 0xc7, 0xda,
-	0xe8, 0x4f, 0xd1, 0x67, 0xc8, 0x7f, 0xaa, 0x9a, 0xf2, 0xe7, 0xe1, 0x27, 0x00, 0x00, 0xff, 0xff,
-	0x11, 0xda, 0x61, 0xe4, 0xc5, 0x01, 0x00, 0x00,
+	// 652 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x4d, 0x6f, 0xd4, 0x3a,
+	0x14, 0x55, 0xe6, 0x3b, 0x77, 0xf4, 0x46, 0xaf, 0x7e, 0xd5, 0x23, 0x1a, 0xf1, 0x51, 0x45, 0xa8,
+	0x1a, 0x89, 0x2a, 0x85, 0x61, 0xd1, 0x0a, 0xb1, 0x01, 0xc1, 0xa2, 0x95, 0x2a, 0x15, 0x77, 0x10,
+	0x12, 0x12, 0x42, 0x9e, 0x89, 0x27, 0x04, 0x92, 0x78, 0xb0, 0x9d, 0x96, 0xf9, 0x27, 0x2c, 0x59,
+	0xf0, 0x13, 0x58, 0xf1, 0xeb, 0x90, 0x6f, 0xec, 0x64, 0xa6, 0x54, 0x88, 0x55, 0xee, 0x3d, 0xf7,
+	0xd8, 0x39, 0x3e, 0xf7, 0xda, 0xb0, 0xa3, 0x55, 0x3c, 0x7f, 0xbf, 0xca, 0xca, 0x24, 0x2d, 0xa2,
+	0x95, 0x14, 0x5a, 0x90, 0x7e, 0x95, 0xa9, 0xf1, 0xbd, 0x44, 0x88, 0x24, 0xe3, 0x87, 0x08, 0xcf,
+	0xcb, 0xe5, 0xa1, 0x4e, 0x73, 0xae, 0x34, 0xcb, 0x57, 0x15, 0x33, 0xfc, 0xe6, 0x81, 0x3f, 0x53,
+	0xf1, 0xfc, 0x55, 0xc9, 0xe5, 0x9a, 0x3c, 0x04, 0xdf, 0x10, 0x24, 0x2b, 0x12, 0x1e, 0x78, 0x7b,
+	0xde, 0x64, 0x38, 0x25, 0x91, 0xdd, 0x2b, 0x9a, 0xb9, 0x0a, 0x6d, 0x48, 0xe4, 0x08, 0x20, 0x66,
+	0x9a, 0x29, 0x51, 0xca, 0x05, 0x0f, 0x5a, 0xb8, 0xe4, 0x56, 0xbd, 0xe4, 0x45, 0x5d, 0x3a, 0x29,
+	0x96, 0x82, 0x6e, 0x50, 0xc9, 0x04, 0xfa, 0x9f, 0x4b, 0x2e, 0x53, 0xae, 0x82, 0xf6, 0x5e, 0x7b,
+	0x32, 0x9c, 0x8e, 0xea, 0x55, 0xa8, 0x85, 0xba, 0x72, 0xf8, 0xd3, 0x83, 0x6e, 0x25, 0x6f, 0x17,
+	0xba, 0x92, 0x2f, 0x4f, 0x62, 0x94, 0xe6, 0xd3, 0x2a, 0x21, 0xb7, 0xc1, 0xcf, 0x45, 0xcc, 0xb3,
+	0x53, 0x25, 0x0a, 0x54, 0xe0, 0xd3, 0x06, 0x20, 0xf7, 0xe1, 0x9f, 0x33, 0xf6, 0xc5, 0x08, 0x39,
+	0x17, 0x69, 0xa1, 0xcd, 0xdf, 0xbc, 0x49, 0x9b, 0x6e, 0x83, 0xe4, 0x2e, 0x40, 0x5a, 0x68, 0x2e,
+	0x2f, 0x59, 0x76, 0xa6, 0x82, 0x0e, 0x52, 0x36, 0x90, 0x6b, 0xc7, 0xec, 0xfe, 0xf5, 0x31, 0xc3,
+	0x77, 0xe0, 0xd7, 0xbe, 0x11, 0x02, 0x9d, 0xa5, 0x14, 0xb9, 0x95, 0x8f, 0x31, 0x19, 0x41, 0x4b,
+	0x0b, 0x2b, 0xbb, 0xa5, 0x05, 0x39, 0x80, 0x76, 0x21, 0xae, 0x50, 0xe5, 0x70, 0x3a, 0x8e, 0xaa,
+	0xfe, 0x45, 0xae, 0x7f, 0xd8, 0x04, 0xec, 0x1f, 0x35, 0xb4, 0x70, 0x06, 0x03, 0xca, 0xd5, 0x4a,
+	0x14, 0x8a, 0x93, 0x00, 0xfa, 0x39, 0x57, 0x8a, 0xd9, 0xd6, 0xf9, 0xd4, 0xa5, 0x24, 0x82, 0xbe,
+	0xe4, 0xaa, 0xcc, 0xb4, 0x0a, 0x5a, 0xe8, 0xf5, 0xee, 0x35, 0xaf, 0xb1, 0x48, 0x1d, 0x29, 0xfc,
+	0xee, 0xc1, 0x70, 0xa3, 0x60, 0x7c, 0xe7, 0x52, 0x0a, 0xe9, 0x7c, 0xc7, 0x84, 0xec, 0xc1, 0x10,
+	0x83, 0x0b, 0x2d, 0xd3, 0x22, 0xb1, 0x47, 0xd8, 0x84, 0x9a, 0x7e, 0xb5, 0x37, 0xfb, 0x35, 0x86,
+	0x41, 0xce, 0x35, 0xc3, 0x76, 0x75, 0xb0, 0x50, 0xe7, 0xe4, 0x01, 0xf4, 0x54, 0x35, 0x14, 0x5d,
+	0x14, 0xfa, 0x5f, 0x33, 0x7d, 0x2a, 0x9e, 0x5f, 0x60, 0x89, 0x5a, 0x4a, 0xf8, 0xb5, 0x05, 0xa3,
+	0x6d, 0xeb, 0x8d, 0xc3, 0x05, 0xcb, 0x9d, 0x01, 0x18, 0x1b, 0x4c, 0xaf, 0x57, 0xdc, 0x0a, 0xc4,
+	0x98, 0xfc, 0x0f, 0x3d, 0xb6, 0x58, 0x70, 0xa5, 0xac, 0x34, 0x9b, 0x91, 0x7f, 0xa1, 0x5d, 0xca,
+	0xcc, 0xca, 0x32, 0xa1, 0x99, 0xae, 0x39, 0x53, 0xe9, 0xe2, 0x59, 0xa9, 0x3f, 0x60, 0xe3, 0x07,
+	0xb4, 0x01, 0xcc, 0x74, 0xd5, 0xc9, 0x6b, 0xc5, 0x65, 0xd0, 0xc3, 0x95, 0xdb, 0x20, 0x39, 0x80,
+	0x9d, 0x1a, 0x38, 0x67, 0x4a, 0x5d, 0x09, 0x19, 0x07, 0x7d, 0x64, 0xfe, 0x5e, 0x30, 0xfe, 0x7c,
+	0x54, 0xa2, 0x30, 0x27, 0x0b, 0x06, 0x95, 0x3f, 0x2e, 0x27, 0xfb, 0x30, 0x52, 0x7c, 0x51, 0x4a,
+	0x7e, 0xea, 0x18, 0x3e, 0x32, 0xae, 0xa1, 0xe1, 0x0f, 0x0f, 0xa0, 0x71, 0xec, 0x46, 0x5b, 0x1e,
+	0x41, 0x47, 0xb3, 0xc4, 0x4d, 0xc4, 0x9d, 0x1b, 0x8c, 0x8e, 0x66, 0x2c, 0x51, 0x2f, 0x0b, 0x2d,
+	0xd7, 0x14, 0xa9, 0x64, 0x1f, 0x7a, 0x2b, 0x77, 0x89, 0xb6, 0xaf, 0x2c, 0x5e, 0x23, 0x6a, 0xab,
+	0xe3, 0x23, 0xf0, 0xeb, 0xa5, 0xc6, 0xd2, 0x4f, 0x7c, 0x6d, 0x7f, 0x6d, 0x42, 0x33, 0x16, 0x97,
+	0x2c, 0x2b, 0x5d, 0x47, 0xaa, 0xe4, 0x49, 0xeb, 0xd8, 0x0b, 0xdf, 0x40, 0x17, 0x77, 0x22, 0xc7,
+	0xd5, 0x43, 0x84, 0x93, 0x6e, 0x1f, 0xa2, 0x3f, 0xdd, 0x85, 0x86, 0xbc, 0xbd, 0xb9, 0x67, 0x37,
+	0x9f, 0x3e, 0xad, 0xec, 0x38, 0x47, 0xb9, 0x24, 0x72, 0x0f, 0x0a, 0xd9, 0x3a, 0x35, 0x62, 0xe3,
+	0x9d, 0x1a, 0x73, 0x37, 0xeb, 0x79, 0xff, 0x6d, 0xb7, 0xfa, 0x69, 0x0f, 0x3f, 0x8f, 0x7f, 0x05,
+	0x00, 0x00, 0xff, 0xff, 0x56, 0x20, 0xfa, 0x8b, 0x73, 0x05, 0x00, 0x00,
 }
 }

+ 50 - 14
pkg/tsdb/models/tsdb_plugin.proto

@@ -3,30 +3,66 @@ option go_package = "proto";
 
 
 package plugins;
 package plugins;
 
 
+import "google/protobuf/timestamp.proto";
+
 // Message represents a simple message sent to the Echo service.
 // Message represents a simple message sent to the Echo service.
-message TsdbRequest {
-  int64 from = 1;
-  int64 to = 2;
-  repeated TsdbQuery queries = 3;
+message TsdbQuery {
+  Timerange timerange = 1;
+  DatasourceInfo datasource = 2;
+  repeated Query queries = 3;
 }
 }
 
 
-message TsdbQuery {
-  string queryJson = 1;
+message Query {
+  string refId = 1;
+  string modelJson = 2;
+  int64 MaxDataPoints = 3;
+  int64 intervalMs = 4;
+  DatasourceInfo datasource = 5;
+}
+
+message Timerange {
+  string from = 1;
+  string to = 2;
+  google.protobuf.Timestamp now = 3;
+}
+
+message Response {
+  string message = 1;
+  repeated QueryResult results = 2;
+}
+
+message QueryResult {
+  string error = 1;
+  string errorString = 2;
+  string refId = 3;
+  string metaJson = 4;
+  repeated TsdbSeries series = 5;
+  //repeat TsdbTables tables = x;    
 }
 }
 
 
-message TsdbResponse {
-    string error = 1;
-    string errorString = 2;
-    string refId = 3;
-    string metaJson = 4;
-    repeated TsdbSeries series = 5;
-    //repeat TsdbTables tables = x;    
+message DatasourceInfo {
+  string name = 1;
+  string type = 2;
+  string access = 3;
+  string url = 4;
+  bool basicAuth = 5;
+  string basicAuthUser = 6;
+  string basicAuthPassword = 7;
+  string jsonData = 8;
+  string secureJsonData = 9;
 }
 }
 
 
 message TsdbSeries {
 message TsdbSeries {
   string name = 1;
   string name = 1;
+  map<string, string> tags = 2;
+  repeated Point points = 3; 
+}
+
+message Point {
+  google.protobuf.Timestamp timestamp = 1;
+  double value = 2;
 }
 }
 
 
 service TsdbPlugin {
 service TsdbPlugin {
-    rpc Get(TsdbRequest) returns (TsdbResponse);
+    rpc Query(TsdbQuery) returns (Response);
 }
 }