Browse Source

Merge branch 'master' into develop

Torkel Ödegaard 8 năm trước cách đây
mục cha
commit
18337f610d
100 tập tin đã thay đổi với 837 bổ sung1848 xóa
  1. 1 0
      .gitignore
  2. 8 0
      CHANGELOG.md
  3. 17 0
      conf/defaults.ini
  4. 17 0
      conf/sample.ini
  5. 6 0
      docker/blocks/jaeger/fig
  6. 1 0
      docker/blocks/prometheus/Dockerfile
  7. 10 0
      docker/blocks/prometheus/alert.rules
  8. 5 0
      docker/blocks/prometheus/fig
  9. 9 0
      docker/blocks/prometheus/prometheus.yml
  10. 3 3
      docs/sources/installation/configuration.md
  11. 10 3
      package.json
  12. 1 1
      pkg/api/api.go
  13. 1 1
      pkg/api/cloudwatch/metrics.go
  14. 2 2
      pkg/api/metrics.go
  15. 16 0
      pkg/api/pluginproxy/ds_proxy.go
  16. 9 1
      pkg/api/route_register.go
  17. 11 0
      pkg/api/route_register_test.go
  18. 1 1
      pkg/cmd/grafana-server/main.go
  19. 11 2
      pkg/cmd/grafana-server/server.go
  20. 23 7
      pkg/metrics/graphitebridge/graphite.go
  21. 68 3
      pkg/metrics/graphitebridge/graphite_test.go
  22. 5 5
      pkg/metrics/metrics.go
  23. 36 0
      pkg/middleware/request_tracing.go
  24. 2 2
      pkg/services/alerting/conditions/query.go
  25. 1 1
      pkg/services/alerting/conditions/query_test.go
  26. 28 1
      pkg/services/alerting/engine.go
  27. 6 1
      pkg/services/alerting/extractor.go
  28. 77 0
      pkg/services/alerting/extractor_test.go
  29. 0 1
      pkg/services/sqlstore/user.go
  30. 116 0
      pkg/tracing/tracing.go
  31. 36 0
      pkg/tracing/tracing_test.go
  32. 0 90
      pkg/tsdb/batch.go
  33. 0 36
      pkg/tsdb/executor.go
  34. 3 3
      pkg/tsdb/fake_test.go
  35. 39 25
      pkg/tsdb/graphite/graphite.go
  36. 23 26
      pkg/tsdb/influxdb/influxdb.go
  37. 5 5
      pkg/tsdb/influxdb/query.go
  38. 7 7
      pkg/tsdb/influxdb/query_part.go
  39. 1 1
      pkg/tsdb/influxdb/query_part_test.go
  40. 3 3
      pkg/tsdb/influxdb/query_test.go
  41. 5 8
      pkg/tsdb/models.go
  42. 0 129
      pkg/tsdb/mqe/httpClient.go
  43. 0 60
      pkg/tsdb/mqe/model_parser.go
  44. 0 127
      pkg/tsdb/mqe/model_parser_test.go
  45. 0 85
      pkg/tsdb/mqe/mqe.go
  46. 0 177
      pkg/tsdb/mqe/response_parser.go
  47. 0 187
      pkg/tsdb/mqe/response_parser_test.go
  48. 0 101
      pkg/tsdb/mqe/token_client.go
  49. 0 27
      pkg/tsdb/mqe/token_client_test.go
  50. 0 137
      pkg/tsdb/mqe/types.go
  51. 0 95
      pkg/tsdb/mqe/types_test.go
  52. 20 18
      pkg/tsdb/mysql/mysql.go
  53. 26 18
      pkg/tsdb/opentsdb/opentsdb.go
  54. 20 16
      pkg/tsdb/prometheus/prometheus.go
  55. 0 21
      pkg/tsdb/query_context.go
  56. 36 0
      pkg/tsdb/query_endpoint.go
  57. 12 49
      pkg/tsdb/request.go
  58. 7 7
      pkg/tsdb/testdata/scenarios.go
  59. 5 5
      pkg/tsdb/testdata/testdata.go
  60. 7 108
      pkg/tsdb/tsdb_test.go
  61. 2 0
      public/app/app.ts
  62. 39 0
      public/app/core/components/PasswordStrength.tsx
  63. 0 58
      public/app/core/components/collapse_box.ts
  64. 1 4
      public/app/core/components/colorpicker.ts
  65. 0 3
      public/app/core/components/dashboard_selector.ts
  66. 0 1
      public/app/core/components/form_dropdown/form_dropdown.ts
  67. 0 2
      public/app/core/components/grafana_app.ts
  68. 1 1
      public/app/core/components/help/help.ts
  69. 0 1
      public/app/core/components/info_popover.ts
  70. 1 1
      public/app/core/components/json_explorer/helpers.ts
  71. 1 2
      public/app/core/components/json_explorer/json_explorer.ts
  72. 0 3
      public/app/core/components/layout_selector/layout_selector.ts
  73. 2 5
      public/app/core/components/navbar/navbar.ts
  74. 0 1
      public/app/core/components/org_switcher.ts
  75. 1 1
      public/app/core/components/row_ctrl.ts
  76. 0 1
      public/app/core/components/scroll/scroll.ts
  77. 1 5
      public/app/core/components/search/search.ts
  78. 1 1
      public/app/core/components/sidemenu/sidemenu.ts
  79. 0 4
      public/app/core/components/switch.ts
  80. 1 2
      public/app/core/components/user_group_picker.ts
  81. 1 2
      public/app/core/components/user_picker.ts
  82. 0 32
      public/app/core/components/wizard/wizard.html
  83. 0 73
      public/app/core/components/wizard/wizard.ts
  84. 3 3
      public/app/core/controllers/signup_ctrl.ts
  85. 2 4
      public/app/core/core.ts
  86. 1 1
      public/app/core/directives/diff-view.ts
  87. 1 1
      public/app/core/directives/password_strength.js
  88. 0 2
      public/app/core/directives/rebuild_on_change.ts
  89. 0 1
      public/app/core/live/live_srv.ts
  90. 0 1
      public/app/core/profiler.ts
  91. 0 1
      public/app/core/routes/routes.ts
  92. 1 2
      public/app/core/services/alert_srv.ts
  93. 1 3
      public/app/core/services/backend_srv.ts
  94. 1 1
      public/app/core/services/dynamic_directive_srv.ts
  95. 1 4
      public/app/core/services/keybindingSrv.ts
  96. 0 2
      public/app/core/services/popover_srv.ts
  97. 0 4
      public/app/core/services/util_srv.ts
  98. 14 0
      public/app/core/specs/PasswordStrength_specs.tsx
  99. 4 3
      public/app/core/time_series2.ts
  100. 0 6
      public/app/core/utils/emitter.ts

+ 1 - 0
.gitignore

@@ -25,6 +25,7 @@ public/css/*.min.css
 .idea/
 .idea/
 *.iml
 *.iml
 *.tmp
 *.tmp
+.DS_Store
 .vscode/
 .vscode/
 
 
 /data/*
 /data/*

+ 8 - 0
CHANGELOG.md

@@ -13,10 +13,17 @@
 * **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
 * **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
 * **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
 * **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
 * **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
 * **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
+* **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213)
+* **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/6710)
 
 
 ## Breaking changes
 ## Breaking changes
 * **Metrics**: The metric structure for internal metrics about Grafana published to graphite has changed. This might break dashboards for internal metrics. 
 * **Metrics**: The metric structure for internal metrics about Grafana published to graphite has changed. This might break dashboards for internal metrics. 
 
 
+# 4.5.2 (unreleased)
+
+## Fixes 
+* **Metrics**: dont write NaN values to graphite [#9279](https://github.com/grafana/grafana/issues/9279)
+
 # 4.5.1 (2017-09-15)
 # 4.5.1 (2017-09-15)
 
 
 ## Fixes
 ## Fixes
@@ -49,6 +56,7 @@
 ### Breaking change
 ### Breaking change
 
 
 * **InfluxDB/Elasticsearch**: The panel & data source option named "Group by time interval" is now named "Min time interval" and does now always define a lower limit for the auto group by time. Without having to use `>` prefix (that prefix still works). This should in theory have close to zero actual impact on existing dashboards. It does mean that if you used this setting to define a hard group by time interval of, say "1d", if you zoomed to a time range wide enough the time range could increase above the "1d" range as the setting is now always considered a lower limit.
 * **InfluxDB/Elasticsearch**: The panel & data source option named "Group by time interval" is now named "Min time interval" and does now always define a lower limit for the auto group by time. Without having to use `>` prefix (that prefix still works). This should in theory have close to zero actual impact on existing dashboards. It does mean that if you used this setting to define a hard group by time interval of, say "1d", if you zoomed to a time range wide enough the time range could increase above the "1d" range as the setting is now always considered a lower limit.
+* **Elasticsearch**: Elasticsearch metric queries without date histogram now return table formated data making table panel much easier to use for this use case. Should not break/change existing dashboards with stock panels but external panel plugins can be affected. 
 
 
 ## Changes
 ## Changes
 
 

+ 17 - 0
conf/defaults.ini

@@ -452,6 +452,23 @@ url = https://grafana.com
 [grafana_com]
 [grafana_com]
 url = https://grafana.com
 url = https://grafana.com
 
 
+#################################### Distributed tracing ############
+[tracing.jaeger]
+# jaeger destination (ex localhost:6831)
+address =
+# tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
+always_included_tag =
+# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
+sampler_type = const
+# jaeger samplerconfig param
+# for "const" sampler, 0 or 1 for always false/true respectively
+# for "probabilistic" sampler, a probability between 0 and 1
+# for "rateLimiting" sampler, the number of spans per second
+# for "remote" sampler, param is the same as for "probabilistic"
+# and indicates the initial sampling rate before the actual one
+# is received from the mothership
+sampler_param = 1
+
 #################################### External Image Storage ##############
 #################################### External Image Storage ##############
 [external_image_storage]
 [external_image_storage]
 # You can choose between (s3, webdav, gcs)
 # You can choose between (s3, webdav, gcs)

+ 17 - 0
conf/sample.ini

@@ -391,6 +391,23 @@
 ;address =
 ;address =
 ;prefix = prod.grafana.%(instance_name)s.
 ;prefix = prod.grafana.%(instance_name)s.
 
 
+#################################### Distributed tracing ############
+[tracing.jaeger]
+# Enable by setting the address sending traces to jaeger (ex localhost:6831)
+;address = localhost:6831
+# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
+;always_included_tag = tag1:value1
+# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
+;sampler_type = const
+# jaeger samplerconfig param
+# for "const" sampler, 0 or 1 for always false/true respectively
+# for "probabilistic" sampler, a probability between 0 and 1
+# for "rateLimiting" sampler, the number of spans per second
+# for "remote" sampler, param is the same as for "probabilistic"
+# and indicates the initial sampling rate before the actual one
+# is received from the mothership
+;sampler_param = 1
+
 #################################### Grafana.com integration  ##########################
 #################################### Grafana.com integration  ##########################
 # Url used to to import dashboards directly from Grafana.com
 # Url used to to import dashboards directly from Grafana.com
 [grafana_com]
 [grafana_com]

+ 6 - 0
docker/blocks/jaeger/fig

@@ -0,0 +1,6 @@
+jaeger:
+  image: jaegertracing/all-in-one:latest
+  ports:
+    - "localhost:6831:6831/udp"
+    - "16686:16686"
+  

+ 1 - 0
docker/blocks/prometheus/Dockerfile

@@ -1,2 +1,3 @@
 FROM prom/prometheus
 FROM prom/prometheus
 ADD prometheus.yml /etc/prometheus/
 ADD prometheus.yml /etc/prometheus/
+ADD alert.rules /etc/prometheus/

+ 10 - 0
docker/blocks/prometheus/alert.rules

@@ -0,0 +1,10 @@
+# Alert Rules
+
+ALERT AppCrash
+  IF process_open_fds > 0
+  FOR 15s
+  LABELS { severity="critical" }
+  ANNOTATIONS {
+   summary = "Number of open fds > 0",
+   description = "Just testing"
+  }

+ 5 - 0
docker/blocks/prometheus/fig

@@ -18,3 +18,8 @@ fake-prometheus-data:
   environment:
   environment:
     FD_DATASOURCE: prom
     FD_DATASOURCE: prom
 
 
+alertmanager:
+  image: quay.io/prometheus/alertmanager
+  net: host
+  ports:
+    - "9093:9093"

+ 9 - 0
docker/blocks/prometheus/prometheus.yml

@@ -6,9 +6,18 @@ global:
 
 
 # Load and evaluate rules in this file every 'evaluation_interval' seconds.
 # Load and evaluate rules in this file every 'evaluation_interval' seconds.
 rule_files:
 rule_files:
+  - "alert.rules"
   # - "first.rules"
   # - "first.rules"
   # - "second.rules"
   # - "second.rules"
 
 
+alerting:
+  alertmanagers:
+  - scheme: http
+    static_configs:
+    - targets:
+      - "127.0.0.1:9093"
+
+
 # A scrape configuration containing exactly one endpoint to scrape:
 # A scrape configuration containing exactly one endpoint to scrape:
 # Here it's Prometheus itself.
 # Here it's Prometheus itself.
 scrape_configs:
 scrape_configs:

+ 3 - 3
docs/sources/installation/configuration.md

@@ -308,15 +308,15 @@ options are `Editor` and `Admin`.
 
 
 ## [auth.github]
 ## [auth.github]
 
 
-You need to create a GitHub application (you find this under the GitHub
-profile page). When you create the application you will need to specify
+You need to create a GitHub OAuth application (you find this under the GitHub
+settings page). When you create the application you will need to specify
 a callback URL. Specify this as callback:
 a callback URL. Specify this as callback:
 
 
     http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/github
     http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/github
 
 
 This callback URL must match the full HTTP address that you use in your
 This callback URL must match the full HTTP address that you use in your
 browser to access Grafana, but with the prefix path of `/login/github`.
 browser to access Grafana, but with the prefix path of `/login/github`.
-When the GitHub application is created you will get a Client ID and a
+When the GitHub OAuth application is created you will get a Client ID and a
 Client Secret. Specify these in the Grafana configuration file. For
 Client Secret. Specify these in the Grafana configuration file. For
 example:
 example:
 
 

+ 10 - 3
package.json

@@ -10,6 +10,8 @@
     "url": "http://github.com/grafana/grafana.git"
     "url": "http://github.com/grafana/grafana.git"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@types/react": "^16.0.5",
+    "@types/react-dom": "^15.5.4",
     "autoprefixer": "^6.4.0",
     "autoprefixer": "^6.4.0",
     "es6-promise": "^3.0.2",
     "es6-promise": "^3.0.2",
     "es6-shim": "^0.35.1",
     "es6-shim": "^0.35.1",
@@ -48,7 +50,7 @@
     "mocha": "3.2.0",
     "mocha": "3.2.0",
     "phantomjs-prebuilt": "^2.1.14",
     "phantomjs-prebuilt": "^2.1.14",
     "reflect-metadata": "0.1.8",
     "reflect-metadata": "0.1.8",
-    "rxjs": "^5.0.0-rc.5",
+    "rxjs": "^5.4.3",
     "sass-lint": "^1.10.2",
     "sass-lint": "^1.10.2",
     "systemjs": "0.19.41",
     "systemjs": "0.19.41",
     "zone.js": "^0.7.2"
     "zone.js": "^0.7.2"
@@ -60,6 +62,7 @@
   },
   },
   "license": "Apache-2.0",
   "license": "Apache-2.0",
   "dependencies": {
   "dependencies": {
+    "@types/enzyme": "^2.8.8",
     "ace-builds": "^1.2.8",
     "ace-builds": "^1.2.8",
     "eventemitter3": "^2.0.2",
     "eventemitter3": "^2.0.2",
     "gaze": "^1.1.2",
     "gaze": "^1.1.2",
@@ -73,13 +76,17 @@
     "karma-sinon": "^1.0.5",
     "karma-sinon": "^1.0.5",
     "lodash": "^4.17.4",
     "lodash": "^4.17.4",
     "mousetrap": "^1.6.0",
     "mousetrap": "^1.6.0",
+    "ngreact": "^0.4.1",
+    "react": "^15.6.1",
+    "react-dom": "^15.6.1",
+    "react-test-renderer": "^15.6.1",
     "remarkable": "^1.7.1",
     "remarkable": "^1.7.1",
     "sinon": "1.17.6",
     "sinon": "1.17.6",
     "systemjs-builder": "^0.15.34",
     "systemjs-builder": "^0.15.34",
     "tether": "^1.4.0",
     "tether": "^1.4.0",
     "tether-drop": "https://github.com/torkelo/drop",
     "tether-drop": "https://github.com/torkelo/drop",
-    "tslint": "^5.1.0",
-    "typescript": "^2.2.2",
+    "tslint": "^5.7.0",
+    "typescript": "^2.5.2",
     "virtual-scroll": "^1.1.1"
     "virtual-scroll": "^1.1.1"
   }
   }
 }
 }

+ 1 - 1
pkg/api/api.go

@@ -21,7 +21,7 @@ func (hs *HttpServer) registerRoutes() {
 	// automatically set HEAD for every GET
 	// automatically set HEAD for every GET
 	macaronR.SetAutoHead(true)
 	macaronR.SetAutoHead(true)
 
 
-	r := newRouteRegister(middleware.RequestMetrics)
+	r := newRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)
 
 
 	// not logged in views
 	// not logged in views
 	r.Get("/", reqSignedIn, Index)
 	r.Get("/", reqSignedIn, Index)

+ 1 - 1
pkg/api/cloudwatch/metrics.go

@@ -126,7 +126,7 @@ func init() {
 		"AWS/NATGateway":       {"NatGatewayId"},
 		"AWS/NATGateway":       {"NatGatewayId"},
 		"AWS/OpsWorks":         {"StackId", "LayerId", "InstanceId"},
 		"AWS/OpsWorks":         {"StackId", "LayerId", "InstanceId"},
 		"AWS/Redshift":         {"NodeID", "ClusterIdentifier"},
 		"AWS/Redshift":         {"NodeID", "ClusterIdentifier"},
-		"AWS/RDS":              {"DBInstanceIdentifier", "DBClusterIdentifier", "DatabaseClass", "EngineName", "Role"},
+		"AWS/RDS":              {"DBInstanceIdentifier", "DBClusterIdentifier", "DbClusterIdentifier", "DatabaseClass", "EngineName", "Role"},
 		"AWS/Route53":          {"HealthCheckId", "Region"},
 		"AWS/Route53":          {"HealthCheckId", "Region"},
 		"AWS/S3":               {"BucketName", "StorageType", "FilterId"},
 		"AWS/S3":               {"BucketName", "StorageType", "FilterId"},
 		"AWS/SES":              {},
 		"AWS/SES":              {},

+ 2 - 2
pkg/api/metrics.go

@@ -31,7 +31,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
 		return ApiError(500, "failed to fetch data source", err)
 		return ApiError(500, "failed to fetch data source", err)
 	}
 	}
 
 
-	request := &tsdb.Request{TimeRange: timeRange}
+	request := &tsdb.TsdbQuery{TimeRange: timeRange}
 
 
 	for _, query := range reqDto.Queries {
 	for _, query := range reqDto.Queries {
 		request.Queries = append(request.Queries, &tsdb.Query{
 		request.Queries = append(request.Queries, &tsdb.Query{
@@ -98,7 +98,7 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
 	intervalMs := c.QueryInt64("intervalMs")
 	intervalMs := c.QueryInt64("intervalMs")
 
 
 	timeRange := tsdb.NewTimeRange(from, to)
 	timeRange := tsdb.NewTimeRange(from, to)
-	request := &tsdb.Request{TimeRange: timeRange}
+	request := &tsdb.TsdbQuery{TimeRange: timeRange}
 
 
 	request.Queries = append(request.Queries, &tsdb.Query{
 	request.Queries = append(request.Queries, &tsdb.Query{
 		RefId:      "A",
 		RefId:      "A",

+ 16 - 0
pkg/api/pluginproxy/ds_proxy.go

@@ -15,6 +15,8 @@ import (
 	"text/template"
 	"text/template"
 	"time"
 	"time"
 
 
+	"github.com/opentracing/opentracing-go"
+
 	"github.com/grafana/grafana/pkg/api/cloudwatch"
 	"github.com/grafana/grafana/pkg/api/cloudwatch"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/middleware"
@@ -85,6 +87,20 @@ func (proxy *DataSourceProxy) HandleRequest() {
 
 
 	proxy.logRequest()
 	proxy.logRequest()
 
 
+	span, ctx := opentracing.StartSpanFromContext(proxy.ctx.Req.Context(), "datasource reverse proxy")
+	proxy.ctx.Req.Request = proxy.ctx.Req.WithContext(ctx)
+
+	defer span.Finish()
+	span.SetTag("datasource_id", proxy.ds.Id)
+	span.SetTag("datasource_type", proxy.ds.Type)
+	span.SetTag("user_id", proxy.ctx.SignedInUser.UserId)
+	span.SetTag("org_id", proxy.ctx.SignedInUser.OrgId)
+
+	opentracing.GlobalTracer().Inject(
+		span.Context(),
+		opentracing.HTTPHeaders,
+		opentracing.HTTPHeadersCarrier(proxy.ctx.Req.Request.Header))
+
 	reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req.Request)
 	reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req.Request)
 	proxy.ctx.Resp.Header().Del("Set-Cookie")
 	proxy.ctx.Resp.Header().Del("Set-Cookie")
 }
 }

+ 9 - 1
pkg/api/route_register.go

@@ -8,6 +8,7 @@ import (
 
 
 type Router interface {
 type Router interface {
 	Handle(method, pattern string, handlers []macaron.Handler) *macaron.Route
 	Handle(method, pattern string, handlers []macaron.Handler) *macaron.Route
+	Get(pattern string, handlers ...macaron.Handler) *macaron.Route
 }
 }
 
 
 type RouteRegister interface {
 type RouteRegister interface {
@@ -62,7 +63,14 @@ func (rr *routeRegister) Group(pattern string, fn func(rr RouteRegister), handle
 
 
 func (rr *routeRegister) Register(router Router) *macaron.Router {
 func (rr *routeRegister) Register(router Router) *macaron.Router {
 	for _, r := range rr.routes {
 	for _, r := range rr.routes {
-		router.Handle(r.method, r.pattern, r.handlers)
+		// GET requests have to be added to macaron routing using Get()
+		// Otherwise HEAD requests will not be allowed.
+		// https://github.com/go-macaron/macaron/blob/a325110f8b392bce3e5cdeb8c44bf98078ada3be/router.go#L198
+		if r.method == http.MethodGet {
+			router.Get(r.pattern, r.handlers...)
+		} else {
+			router.Handle(r.method, r.pattern, r.handlers)
+		}
 	}
 	}
 
 
 	for _, g := range rr.groups {
 	for _, g := range rr.groups {

+ 11 - 0
pkg/api/route_register_test.go

@@ -1,6 +1,7 @@
 package api
 package api
 
 
 import (
 import (
+	"net/http"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
 
 
@@ -21,6 +22,16 @@ func (fr *fakeRouter) Handle(method, pattern string, handlers []macaron.Handler)
 	return &macaron.Route{}
 	return &macaron.Route{}
 }
 }
 
 
+func (fr *fakeRouter) Get(pattern string, handlers ...macaron.Handler) *macaron.Route {
+	fr.route = append(fr.route, route{
+		pattern:  pattern,
+		method:   http.MethodGet,
+		handlers: handlers,
+	})
+
+	return &macaron.Route{}
+}
+
 func emptyHandlers(n int) []macaron.Handler {
 func emptyHandlers(n int) []macaron.Handler {
 	res := []macaron.Handler{}
 	res := []macaron.Handler{}
 	for i := 1; n >= i; i++ {
 	for i := 1; n >= i; i++ {

+ 1 - 1
pkg/cmd/grafana-server/main.go

@@ -22,9 +22,9 @@ import (
 	_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
 	_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
 	_ "github.com/grafana/grafana/pkg/tsdb/graphite"
 	_ "github.com/grafana/grafana/pkg/tsdb/graphite"
 	_ "github.com/grafana/grafana/pkg/tsdb/influxdb"
 	_ "github.com/grafana/grafana/pkg/tsdb/influxdb"
-	_ "github.com/grafana/grafana/pkg/tsdb/mqe"
 	_ "github.com/grafana/grafana/pkg/tsdb/mysql"
 	_ "github.com/grafana/grafana/pkg/tsdb/mysql"
 	_ "github.com/grafana/grafana/pkg/tsdb/opentsdb"
 	_ "github.com/grafana/grafana/pkg/tsdb/opentsdb"
+
 	_ "github.com/grafana/grafana/pkg/tsdb/prometheus"
 	_ "github.com/grafana/grafana/pkg/tsdb/prometheus"
 	_ "github.com/grafana/grafana/pkg/tsdb/testdata"
 	_ "github.com/grafana/grafana/pkg/tsdb/testdata"
 )
 )

+ 11 - 2
pkg/cmd/grafana-server/server.go

@@ -24,6 +24,7 @@ import (
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/social"
 	"github.com/grafana/grafana/pkg/social"
+	"github.com/grafana/grafana/pkg/tracing"
 )
 )
 
 
 func NewGrafanaServer() models.GrafanaServer {
 func NewGrafanaServer() models.GrafanaServer {
@@ -61,6 +62,14 @@ func (g *GrafanaServerImpl) Start() {
 	eventpublisher.Init()
 	eventpublisher.Init()
 	plugins.Init()
 	plugins.Init()
 
 
+	closer, err := tracing.Init(setting.Cfg)
+	if err != nil {
+		g.log.Error("Tracing settings is not valid", "error", err)
+		g.Shutdown(1, "Startup failed")
+		return
+	}
+	defer closer.Close()
+
 	// init alerting
 	// init alerting
 	if setting.AlertingEnabled && setting.ExecuteAlerts {
 	if setting.AlertingEnabled && setting.ExecuteAlerts {
 		engine := alerting.NewEngine()
 		engine := alerting.NewEngine()
@@ -71,8 +80,8 @@ func (g *GrafanaServerImpl) Start() {
 	cleanUpService := cleanup.NewCleanUpService()
 	cleanUpService := cleanup.NewCleanUpService()
 	g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) })
 	g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) })
 
 
-	if err := notifications.Init(); err != nil {
-		g.log.Error("Notification service failed to initialize", "erro", err)
+	if err = notifications.Init(); err != nil {
+		g.log.Error("Notification service failed to initialize", "error", err)
 		g.Shutdown(1, "Startup failed")
 		g.Shutdown(1, "Startup failed")
 		return
 		return
 	}
 	}

+ 23 - 7
pkg/metrics/graphitebridge/graphite.go

@@ -20,6 +20,7 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"math"
 	"net"
 	"net"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
@@ -53,7 +54,17 @@ const (
 	AbortOnError
 	AbortOnError
 )
 )
 
 
-var metricCategoryPrefix []string = []string{"proxy_", "api_", "page_", "alerting_", "aws_", "db_", "stat_", "go_", "process_"}
+var metricCategoryPrefix []string = []string{
+	"proxy_",
+	"api_",
+	"page_",
+	"alerting_",
+	"aws_",
+	"db_",
+	"stat_",
+	"go_",
+	"process_"}
+
 var trimMetricPrefix []string = []string{"grafana_"}
 var trimMetricPrefix []string = []string{"grafana_"}
 
 
 // Config defines the Graphite bridge config.
 // Config defines the Graphite bridge config.
@@ -208,6 +219,10 @@ func (b *Bridge) writeMetrics(w io.Writer, mfs []*dto.MetricFamily, prefix strin
 
 
 		buf := bufio.NewWriter(w)
 		buf := bufio.NewWriter(w)
 		for _, s := range vec {
 		for _, s := range vec {
+			if math.IsNaN(float64(s.Value)) {
+				continue
+			}
+
 			if err := writePrefix(buf, prefix); err != nil {
 			if err := writePrefix(buf, prefix); err != nil {
 				return err
 				return err
 			}
 			}
@@ -235,16 +250,17 @@ func writeMetric(buf *bufio.Writer, m model.Metric, mf *dto.MetricFamily) error
 	if !hasName {
 	if !hasName {
 		numLabels = len(m)
 		numLabels = len(m)
 	}
 	}
-	for _, v := range metricCategoryPrefix {
+
+	for _, v := range trimMetricPrefix {
 		if strings.HasPrefix(string(metricName), v) {
 		if strings.HasPrefix(string(metricName), v) {
-			group := strings.Replace(v, "_", " ", 1)
-			metricName = model.LabelValue(strings.Replace(string(metricName), v, group, 1))
+			metricName = model.LabelValue(strings.Replace(string(metricName), v, "", 1))
 		}
 		}
 	}
 	}
 
 
-	for _, v := range trimMetricPrefix {
+	for _, v := range metricCategoryPrefix {
 		if strings.HasPrefix(string(metricName), v) {
 		if strings.HasPrefix(string(metricName), v) {
-			metricName = model.LabelValue(strings.Replace(string(metricName), v, "", 1))
+			group := strings.Replace(v, "_", " ", 1)
+			metricName = model.LabelValue(strings.Replace(string(metricName), v, group, 1))
 		}
 		}
 	}
 	}
 
 
@@ -357,7 +373,7 @@ func replaceInvalidRune(c rune) rune {
 	if c == ' ' {
 	if c == ' ' {
 		return '.'
 		return '.'
 	}
 	}
-	if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == ':' || (c >= '0' && c <= '9')) {
+	if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == ':' || (c >= '0' && c <= '9')) {
 		return '_'
 		return '_'
 	}
 	}
 	return c
 	return c

+ 68 - 3
pkg/metrics/graphitebridge/graphite_test.go

@@ -128,6 +128,7 @@ func TestWriteSummary(t *testing.T) {
 		prometheus.SummaryOpts{
 		prometheus.SummaryOpts{
 			Name:        "name",
 			Name:        "name",
 			Help:        "docstring",
 			Help:        "docstring",
+			Namespace:   "grafana",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 			Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
 		},
 		},
@@ -187,6 +188,7 @@ func TestWriteHistogram(t *testing.T) {
 		prometheus.HistogramOpts{
 		prometheus.HistogramOpts{
 			Name:        "name",
 			Name:        "name",
 			Help:        "docstring",
 			Help:        "docstring",
+			Namespace:   "grafana",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			Buckets:     []float64{0.01, 0.02, 0.05, 0.1},
 			Buckets:     []float64{0.01, 0.02, 0.05, 0.1},
 		},
 		},
@@ -248,6 +250,17 @@ func TestCounterVec(t *testing.T) {
 	cntVec := prometheus.NewCounterVec(
 	cntVec := prometheus.NewCounterVec(
 		prometheus.CounterOpts{
 		prometheus.CounterOpts{
 			Name:        "page_response",
 			Name:        "page_response",
+			Namespace:   "grafana",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+		},
+		[]string{"labelname"},
+	)
+
+	apicntVec := prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name:        "api_response",
+			Namespace:   "grafana",
 			Help:        "docstring",
 			Help:        "docstring",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 		},
 		},
@@ -256,9 +269,12 @@ func TestCounterVec(t *testing.T) {
 
 
 	reg := prometheus.NewRegistry()
 	reg := prometheus.NewRegistry()
 	reg.MustRegister(cntVec)
 	reg.MustRegister(cntVec)
+	reg.MustRegister(apicntVec)
 
 
 	cntVec.WithLabelValues("val1").Inc()
 	cntVec.WithLabelValues("val1").Inc()
 	cntVec.WithLabelValues("val2").Inc()
 	cntVec.WithLabelValues("val2").Inc()
+	apicntVec.WithLabelValues("val1").Inc()
+	apicntVec.WithLabelValues("val2").Inc()
 
 
 	b, err := NewBridge(&Config{
 	b, err := NewBridge(&Config{
 		URL:             "localhost:8080",
 		URL:             "localhost:8080",
@@ -281,7 +297,9 @@ func TestCounterVec(t *testing.T) {
 		t.Fatalf("error: %v", err)
 		t.Fatalf("error: %v", err)
 	}
 	}
 
 
-	want := `prefix.page.response.constname.constvalue.labelname.val1.count 1 1477043
+	want := `prefix.api.response.constname.constvalue.labelname.val1.count 1 1477043
+prefix.api.response.constname.constvalue.labelname.val2.count 1 1477043
+prefix.page.response.constname.constvalue.labelname.val1.count 1 1477043
 prefix.page.response.constname.constvalue.labelname.val2.count 1 1477043
 prefix.page.response.constname.constvalue.labelname.val2.count 1 1477043
 `
 `
 	if got := buf.String(); want != got {
 	if got := buf.String(); want != got {
@@ -291,6 +309,8 @@ prefix.page.response.constname.constvalue.labelname.val2.count 1 1477043
 	//next collect
 	//next collect
 	cntVec.WithLabelValues("val1").Inc()
 	cntVec.WithLabelValues("val1").Inc()
 	cntVec.WithLabelValues("val2").Inc()
 	cntVec.WithLabelValues("val2").Inc()
+	apicntVec.WithLabelValues("val1").Inc()
+	apicntVec.WithLabelValues("val2").Inc()
 
 
 	mfs, err = reg.Gather()
 	mfs, err = reg.Gather()
 	if err != nil {
 	if err != nil {
@@ -303,7 +323,9 @@ prefix.page.response.constname.constvalue.labelname.val2.count 1 1477043
 		t.Fatalf("error: %v", err)
 		t.Fatalf("error: %v", err)
 	}
 	}
 
 
-	want2 := `prefix.page.response.constname.constvalue.labelname.val1.count 1 1477053
+	want2 := `prefix.api.response.constname.constvalue.labelname.val1.count 1 1477053
+prefix.api.response.constname.constvalue.labelname.val2.count 1 1477053
+prefix.page.response.constname.constvalue.labelname.val1.count 1 1477053
 prefix.page.response.constname.constvalue.labelname.val2.count 1 1477053
 prefix.page.response.constname.constvalue.labelname.val2.count 1 1477053
 `
 `
 	if got := buf.String(); want2 != got {
 	if got := buf.String(); want2 != got {
@@ -316,6 +338,7 @@ func TestCounter(t *testing.T) {
 		prometheus.CounterOpts{
 		prometheus.CounterOpts{
 			Name:        "page_response",
 			Name:        "page_response",
 			Help:        "docstring",
 			Help:        "docstring",
+			Namespace:   "grafana",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 		})
 		})
 
 
@@ -373,7 +396,7 @@ func TestCounter(t *testing.T) {
 func TestTrimGrafanaNamespace(t *testing.T) {
 func TestTrimGrafanaNamespace(t *testing.T) {
 	cntVec := prometheus.NewCounter(
 	cntVec := prometheus.NewCounter(
 		prometheus.CounterOpts{
 		prometheus.CounterOpts{
-			Name:        "grafana_http_request_total",
+			Name:        "http_request_total",
 			Help:        "docstring",
 			Help:        "docstring",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 		})
 		})
@@ -410,12 +433,54 @@ func TestTrimGrafanaNamespace(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestSkipNanValues(t *testing.T) {
+	cntVec := prometheus.NewSummary(
+		prometheus.SummaryOpts{
+			Name:        "http_request_total",
+			Help:        "docstring",
+			ConstLabels: prometheus.Labels{"constname": "constvalue"},
+		})
+
+	reg := prometheus.NewRegistry()
+	reg.MustRegister(cntVec)
+
+	b, err := NewBridge(&Config{
+		URL:             "localhost:8080",
+		Gatherer:        reg,
+		CountersAsDelta: true,
+	})
+	if err != nil {
+		t.Fatalf("error creating bridge: %v", err)
+	}
+
+	// first collect
+	mfs, err := reg.Gather()
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	var buf bytes.Buffer
+	err = b.writeMetrics(&buf, mfs, "prefix.", model.Time(1477043083))
+	if err != nil {
+		t.Fatalf("error: %v", err)
+	}
+
+	want := `prefix.http_request_total_sum.constname.constvalue 0 1477043
+prefix.http_request_total_count.constname.constvalue.count 0 1477043
+`
+
+	if got := buf.String(); want != got {
+		t.Fatalf("wanted \n%s\n, got \n%s\n", want, got)
+	}
+}
+
 func TestPush(t *testing.T) {
 func TestPush(t *testing.T) {
 	reg := prometheus.NewRegistry()
 	reg := prometheus.NewRegistry()
 	cntVec := prometheus.NewCounterVec(
 	cntVec := prometheus.NewCounterVec(
 		prometheus.CounterOpts{
 		prometheus.CounterOpts{
 			Name:        "name",
 			Name:        "name",
 			Help:        "docstring",
 			Help:        "docstring",
+			Namespace:   "grafana",
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 			ConstLabels: prometheus.Labels{"constname": "constvalue"},
 		},
 		},
 		[]string{"labelname"},
 		[]string{"labelname"},

+ 5 - 5
pkg/metrics/metrics.go

@@ -102,7 +102,7 @@ func init() {
 
 
 	M_Http_Request_Summary = prometheus.NewSummaryVec(
 	M_Http_Request_Summary = prometheus.NewSummaryVec(
 		prometheus.SummaryOpts{
 		prometheus.SummaryOpts{
-			Name: "http_request_duration_milleseconds",
+			Name: "http_request_duration_milliseconds",
 			Help: "http request summary",
 			Help: "http request summary",
 		},
 		},
 		[]string{"handler", "statuscode", "method"},
 		[]string{"handler", "statuscode", "method"},
@@ -127,19 +127,19 @@ func init() {
 	})
 	})
 
 
 	M_Api_Dashboard_Save = prometheus.NewSummary(prometheus.SummaryOpts{
 	M_Api_Dashboard_Save = prometheus.NewSummary(prometheus.SummaryOpts{
-		Name:      "api_dashboard_save_milleseconds",
+		Name:      "api_dashboard_save_milliseconds",
 		Help:      "summary for dashboard save duration",
 		Help:      "summary for dashboard save duration",
 		Namespace: exporterName,
 		Namespace: exporterName,
 	})
 	})
 
 
 	M_Api_Dashboard_Get = prometheus.NewSummary(prometheus.SummaryOpts{
 	M_Api_Dashboard_Get = prometheus.NewSummary(prometheus.SummaryOpts{
-		Name:      "api_dashboard_get_milleseconds",
+		Name:      "api_dashboard_get_milliseconds",
 		Help:      "summary for dashboard get duration",
 		Help:      "summary for dashboard get duration",
 		Namespace: exporterName,
 		Namespace: exporterName,
 	})
 	})
 
 
 	M_Api_Dashboard_Search = prometheus.NewSummary(prometheus.SummaryOpts{
 	M_Api_Dashboard_Search = prometheus.NewSummary(prometheus.SummaryOpts{
-		Name:      "api_dashboard_search_milleseconds",
+		Name:      "api_dashboard_search_milliseconds",
 		Help:      "summary for dashboard search duration",
 		Help:      "summary for dashboard search duration",
 		Namespace: exporterName,
 		Namespace: exporterName,
 	})
 	})
@@ -223,7 +223,7 @@ func init() {
 	})
 	})
 
 
 	M_DataSource_ProxyReq_Timer = prometheus.NewSummary(prometheus.SummaryOpts{
 	M_DataSource_ProxyReq_Timer = prometheus.NewSummary(prometheus.SummaryOpts{
-		Name:      "api_dataproxy_request_all_milleseconds",
+		Name:      "api_dataproxy_request_all_milliseconds",
 		Help:      "summary for dashboard search duration",
 		Help:      "summary for dashboard search duration",
 		Namespace: exporterName,
 		Namespace: exporterName,
 	})
 	})

+ 36 - 0
pkg/middleware/request_tracing.go

@@ -0,0 +1,36 @@
+package middleware
+
+import (
+	"fmt"
+	"net/http"
+
+	opentracing "github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+
+	"gopkg.in/macaron.v1"
+)
+
+func RequestTracing(handler string) macaron.Handler {
+	return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
+		rw := res.(macaron.ResponseWriter)
+
+		tracer := opentracing.GlobalTracer()
+		wireContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
+		span := tracer.StartSpan(fmt.Sprintf("HTTP %s", handler), ext.RPCServerOption(wireContext))
+		defer span.Finish()
+
+		ctx := opentracing.ContextWithSpan(req.Context(), span)
+		c.Req.Request = req.WithContext(ctx)
+
+		c.Next()
+
+		status := rw.Status()
+
+		ext.HTTPStatusCode.Set(span, uint16(status))
+		ext.HTTPUrl.Set(span, req.RequestURI)
+		ext.HTTPMethod.Set(span, req.Method)
+		if status >= 400 {
+			ext.Error.Set(span, true)
+		}
+	}
+}

+ 2 - 2
pkg/services/alerting/conditions/query.go

@@ -139,8 +139,8 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
 	return result, nil
 	return result, nil
 }
 }
 
 
-func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timeRange *tsdb.TimeRange) *tsdb.Request {
-	req := &tsdb.Request{
+func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timeRange *tsdb.TimeRange) *tsdb.TsdbQuery {
+	req := &tsdb.TsdbQuery{
 		TimeRange: timeRange,
 		TimeRange: timeRange,
 		Queries: []*tsdb.Query{
 		Queries: []*tsdb.Query{
 			{
 			{

+ 1 - 1
pkg/services/alerting/conditions/query_test.go

@@ -168,7 +168,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
 
 
 	ctx.condition = condition
 	ctx.condition = condition
 
 
-	condition.HandleRequest = func(context context.Context, req *tsdb.Request) (*tsdb.Response, error) {
+	condition.HandleRequest = func(context context.Context, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
 		return &tsdb.Response{
 		return &tsdb.Response{
 			Results: map[string]*tsdb.QueryResult{
 			Results: map[string]*tsdb.QueryResult{
 				"A": {Series: ctx.series},
 				"A": {Series: ctx.series},

+ 28 - 1
pkg/services/alerting/engine.go

@@ -2,8 +2,13 @@ package alerting
 
 
 import (
 import (
 	"context"
 	"context"
+	"fmt"
 	"time"
 	"time"
 
 
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+	tlog "github.com/opentracing/opentracing-go/log"
+
 	"github.com/benbjohnson/clock"
 	"github.com/benbjohnson/clock"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/sync/errgroup"
@@ -99,22 +104,44 @@ func (e *Engine) processJob(grafanaCtx context.Context, job *Job) error {
 	}()
 	}()
 
 
 	alertCtx, cancelFn := context.WithTimeout(context.Background(), alertTimeout)
 	alertCtx, cancelFn := context.WithTimeout(context.Background(), alertTimeout)
+	span := opentracing.StartSpan("alert execution")
+	alertCtx = opentracing.ContextWithSpan(alertCtx, span)
 
 
 	job.Running = true
 	job.Running = true
 	evalContext := NewEvalContext(alertCtx, job.Rule)
 	evalContext := NewEvalContext(alertCtx, job.Rule)
+	evalContext.Ctx = alertCtx
 
 
 	done := make(chan struct{})
 	done := make(chan struct{})
-
 	go func() {
 	go func() {
 		defer func() {
 		defer func() {
 			if err := recover(); err != nil {
 			if err := recover(); err != nil {
 				e.log.Error("Alert Panic", "error", err, "stack", log.Stack(1))
 				e.log.Error("Alert Panic", "error", err, "stack", log.Stack(1))
+				ext.Error.Set(span, true)
+				span.LogFields(
+					tlog.Error(fmt.Errorf("%v", err)),
+					tlog.String("message", "failed to execute alert rule. panic was recovered."),
+				)
+				span.Finish()
 				close(done)
 				close(done)
 			}
 			}
 		}()
 		}()
 
 
 		e.evalHandler.Eval(evalContext)
 		e.evalHandler.Eval(evalContext)
 		e.resultHandler.Handle(evalContext)
 		e.resultHandler.Handle(evalContext)
+
+		span.SetTag("alertId", evalContext.Rule.Id)
+		span.SetTag("dashboardId", evalContext.Rule.DashboardId)
+		span.SetTag("firing", evalContext.Firing)
+		span.SetTag("nodatapoints", evalContext.NoDataFound)
+		if evalContext.Error != nil {
+			ext.Error.Set(span, true)
+			span.LogFields(
+				tlog.Error(evalContext.Error),
+				tlog.String("message", "alerting execution failed"),
+			)
+		}
+
+		span.Finish()
 		close(done)
 		close(done)
 	}()
 	}()
 
 

+ 6 - 1
pkg/services/alerting/extractor.go

@@ -89,6 +89,11 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
 				continue
 				continue
 			}
 			}
 
 
+			panelId, err := panel.Get("id").Int64()
+			if err != nil {
+				return nil, fmt.Errorf("panel id is required. err %v", err)
+			}
+
 			// backward compatibility check, can be removed later
 			// backward compatibility check, can be removed later
 			enabled, hasEnabled := jsonAlert.CheckGet("enabled")
 			enabled, hasEnabled := jsonAlert.CheckGet("enabled")
 			if hasEnabled && enabled.MustBool() == false {
 			if hasEnabled && enabled.MustBool() == false {
@@ -103,7 +108,7 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
 			alert := &m.Alert{
 			alert := &m.Alert{
 				DashboardId: e.Dash.Id,
 				DashboardId: e.Dash.Id,
 				OrgId:       e.OrgId,
 				OrgId:       e.OrgId,
-				PanelId:     panel.Get("id").MustInt64(),
+				PanelId:     panelId,
 				Id:          jsonAlert.Get("id").MustInt64(),
 				Id:          jsonAlert.Get("id").MustInt64(),
 				Name:        jsonAlert.Get("name").MustString(),
 				Name:        jsonAlert.Get("name").MustString(),
 				Handler:     jsonAlert.Get("handler").MustInt64(),
 				Handler:     jsonAlert.Get("handler").MustInt64(),

+ 77 - 0
pkg/services/alerting/extractor_test.go

@@ -200,6 +200,83 @@ func TestAlertRuleExtraction(t *testing.T) {
 			})
 			})
 		})
 		})
 
 
+		Convey("Panels missing id should return error", func() {
+			panelWithoutId := `
+      {
+        "id": 57,
+        "title": "Graphite 4",
+        "originalTitle": "Graphite 4",
+        "tags": ["graphite"],
+        "rows": [
+        {
+          "panels": [
+          {
+            "title": "Active desktop users",
+            "editable": true,
+            "type": "graph",
+            "targets": [
+            {
+              "refId": "A",
+              "target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
+            }
+            ],
+            "datasource": null,
+            "alert": {
+              "name": "name1",
+              "message": "desc1",
+              "handler": 1,
+              "frequency": "60s",
+              "conditions": [
+              {
+                "type": "query",
+                "query": {"params": ["A", "5m", "now"]},
+                "reducer": {"type": "avg", "params": []},
+                "evaluator": {"type": ">", "params": [100]}
+              }
+              ]
+            }
+          },
+          {
+            "title": "Active mobile users",
+            "id": 4,
+            "targets": [
+              {"refId": "A", "target": ""},
+              {"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
+            ],
+            "datasource": "graphite2",
+            "alert": {
+              "name": "name2",
+              "message": "desc2",
+              "handler": 0,
+              "frequency": "60s",
+              "severity": "warning",
+              "conditions": [
+              {
+                "type": "query",
+                "query":  {"params": ["B", "5m", "now"]},
+                "reducer": {"type": "avg", "params": []},
+                "evaluator": {"type": ">", "params": [100]}
+              }
+              ]
+            }
+          }
+          ]
+        }
+      ]
+			}`
+
+			dashJson, err := simplejson.NewJson([]byte(panelWithoutId))
+			So(err, ShouldBeNil)
+			dash := m.NewDashboardFromJson(dashJson)
+			extractor := NewDashAlertExtractor(dash, 1)
+
+			_, err = extractor.GetAlerts()
+
+			Convey("panels without Id should return error", func() {
+				So(err, ShouldNotBeNil)
+			})
+		})
+
 		Convey("Parse and validate dashboard containing influxdb alert", func() {
 		Convey("Parse and validate dashboard containing influxdb alert", func() {
 
 
 			json2 := `{
 			json2 := `{

+ 0 - 1
pkg/services/sqlstore/user.go

@@ -28,7 +28,6 @@ func init() {
 	bus.AddHandler("sql", SearchUsers)
 	bus.AddHandler("sql", SearchUsers)
 	bus.AddHandler("sql", GetUserOrgList)
 	bus.AddHandler("sql", GetUserOrgList)
 	bus.AddHandler("sql", DeleteUser)
 	bus.AddHandler("sql", DeleteUser)
-	bus.AddHandler("sql", SetUsingOrg)
 	bus.AddHandler("sql", UpdateUserPermissions)
 	bus.AddHandler("sql", UpdateUserPermissions)
 	bus.AddHandler("sql", SetUserHelpFlag)
 	bus.AddHandler("sql", SetUserHelpFlag)
 }
 }

+ 116 - 0
pkg/tracing/tracing.go

@@ -0,0 +1,116 @@
+package tracing
+
+import (
+	"io"
+	"strings"
+
+	"github.com/grafana/grafana/pkg/log"
+	"github.com/grafana/grafana/pkg/setting"
+
+	opentracing "github.com/opentracing/opentracing-go"
+	jaegercfg "github.com/uber/jaeger-client-go/config"
+	ini "gopkg.in/ini.v1"
+)
+
+var (
+	logger log.Logger = log.New("tracing")
+)
+
+type TracingSettings struct {
+	Enabled      bool
+	Address      string
+	CustomTags   map[string]string
+	SamplerType  string
+	SamplerParam float64
+}
+
+func Init(file *ini.File) (io.Closer, error) {
+	settings := parseSettings(file)
+	return internalInit(settings)
+}
+
+func parseSettings(file *ini.File) *TracingSettings {
+	settings := &TracingSettings{}
+
+	var section, err = setting.Cfg.GetSection("tracing.jaeger")
+	if err != nil {
+		return settings
+	}
+
+	settings.Address = section.Key("address").MustString("")
+	if settings.Address != "" {
+		settings.Enabled = true
+	}
+
+	settings.CustomTags = splitTagSettings(section.Key("always_included_tag").MustString(""))
+	settings.SamplerType = section.Key("sampler_type").MustString("")
+	settings.SamplerParam = section.Key("sampler_param").MustFloat64(1)
+
+	return settings
+}
+
+func internalInit(settings *TracingSettings) (io.Closer, error) {
+	if !settings.Enabled {
+		return &nullCloser{}, nil
+	}
+
+	cfg := jaegercfg.Configuration{
+		Disabled: !settings.Enabled,
+		Sampler: &jaegercfg.SamplerConfig{
+			Type:  settings.SamplerType,
+			Param: settings.SamplerParam,
+		},
+		Reporter: &jaegercfg.ReporterConfig{
+			LogSpans:           false,
+			LocalAgentHostPort: settings.Address,
+		},
+	}
+
+	jLogger := &jaegerLogWrapper{logger: log.New("jaeger")}
+
+	options := []jaegercfg.Option{}
+	options = append(options, jaegercfg.Logger(jLogger))
+
+	for tag, value := range settings.CustomTags {
+		options = append(options, jaegercfg.Tag(tag, value))
+	}
+
+	tracer, closer, err := cfg.New("grafana", options...)
+	if err != nil {
+		return nil, err
+	}
+
+	logger.Info("Initialized jaeger tracer", "address", settings.Address)
+	opentracing.InitGlobalTracer(tracer)
+	return closer, nil
+}
+
+func splitTagSettings(input string) map[string]string {
+	res := map[string]string{}
+
+	tags := strings.Split(input, ",")
+	for _, v := range tags {
+		kv := strings.Split(v, ":")
+		if len(kv) > 1 {
+			res[kv[0]] = kv[1]
+		}
+	}
+
+	return res
+}
+
+type jaegerLogWrapper struct {
+	logger log.Logger
+}
+
+func (jlw *jaegerLogWrapper) Error(msg string) {
+	jlw.logger.Error(msg)
+}
+
+func (jlw *jaegerLogWrapper) Infof(msg string, args ...interface{}) {
+	jlw.logger.Info(msg, args)
+}
+
+type nullCloser struct{}
+
+func (*nullCloser) Close() error { return nil }

+ 36 - 0
pkg/tracing/tracing_test.go

@@ -0,0 +1,36 @@
+package tracing
+
+import "testing"
+
+func TestGroupSplit(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected map[string]string
+	}{
+		{
+			input: "tag1:value1,tag2:value2",
+			expected: map[string]string{
+				"tag1": "value1",
+				"tag2": "value2",
+			},
+		},
+		{
+			input:    "",
+			expected: map[string]string{},
+		},
+		{
+			input:    "tag1",
+			expected: map[string]string{},
+		},
+	}
+
+	for _, test := range tests {
+		tags := splitTagSettings(test.input)
+		for k, v := range test.expected {
+			value, exists := tags[k]
+			if !exists || value != v {
+				t.Errorf("tags does not match %v ", test)
+			}
+		}
+	}
+}

+ 0 - 90
pkg/tsdb/batch.go

@@ -1,90 +0,0 @@
-package tsdb
-
-import "context"
-
-type Batch struct {
-	DataSourceId int64
-	Queries      QuerySlice
-	Depends      map[string]bool
-	Done         bool
-	Started      bool
-}
-
-type BatchSlice []*Batch
-
-func newBatch(dsId int64, queries QuerySlice) *Batch {
-	return &Batch{
-		DataSourceId: dsId,
-		Queries:      queries,
-		Depends:      make(map[string]bool),
-	}
-}
-
-func (bg *Batch) process(ctx context.Context, queryContext *QueryContext) {
-	executor, err := getExecutorFor(bg.Queries[0].DataSource)
-
-	if err != nil {
-		bg.Done = true
-		result := &BatchResult{
-			Error:        err,
-			QueryResults: make(map[string]*QueryResult),
-		}
-		for _, query := range bg.Queries {
-			result.QueryResults[query.RefId] = &QueryResult{Error: result.Error}
-		}
-		queryContext.ResultsChan <- result
-		return
-	}
-
-	res := executor.Execute(ctx, bg.Queries, queryContext)
-	bg.Done = true
-	queryContext.ResultsChan <- res
-}
-
-func (bg *Batch) addQuery(query *Query) {
-	bg.Queries = append(bg.Queries, query)
-}
-
-func (bg *Batch) allDependenciesAreIn(context *QueryContext) bool {
-	for key := range bg.Depends {
-		if _, exists := context.Results[key]; !exists {
-			return false
-		}
-	}
-
-	return true
-}
-
-func getBatches(req *Request) (BatchSlice, error) {
-	batches := make(BatchSlice, 0)
-
-	for _, query := range req.Queries {
-		if foundBatch := findMatchingBatchGroup(query, batches); foundBatch != nil {
-			foundBatch.addQuery(query)
-		} else {
-			newBatch := newBatch(query.DataSource.Id, QuerySlice{query})
-			batches = append(batches, newBatch)
-
-			for _, refId := range query.Depends {
-				for _, batch := range batches {
-					for _, batchQuery := range batch.Queries {
-						if batchQuery.RefId == refId {
-							newBatch.Depends[refId] = true
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return batches, nil
-}
-
-func findMatchingBatchGroup(query *Query, batches BatchSlice) *Batch {
-	for _, batch := range batches {
-		if batch.DataSourceId == query.DataSource.Id {
-			return batch
-		}
-	}
-	return nil
-}

+ 0 - 36
pkg/tsdb/executor.go

@@ -1,36 +0,0 @@
-package tsdb
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/grafana/grafana/pkg/models"
-)
-
-type Executor interface {
-	Execute(ctx context.Context, queries QuerySlice, query *QueryContext) *BatchResult
-}
-
-var registry map[string]GetExecutorFn
-
-type GetExecutorFn func(dsInfo *models.DataSource) (Executor, error)
-
-func init() {
-	registry = make(map[string]GetExecutorFn)
-}
-
-func getExecutorFor(dsInfo *models.DataSource) (Executor, error) {
-	if fn, exists := registry[dsInfo.Type]; exists {
-		executor, err := fn(dsInfo)
-		if err != nil {
-			return nil, err
-		}
-
-		return executor, nil
-	}
-	return nil, fmt.Errorf("Could not find executor for data source type: %s", dsInfo.Type)
-}
-
-func RegisterExecutor(pluginId string, fn GetExecutorFn) {
-	registry[pluginId] = fn
-}

+ 3 - 3
pkg/tsdb/fake_test.go

@@ -11,7 +11,7 @@ type FakeExecutor struct {
 	resultsFn map[string]ResultsFn
 	resultsFn map[string]ResultsFn
 }
 }
 
 
-type ResultsFn func(context *QueryContext) *QueryResult
+type ResultsFn func(context *TsdbQuery) *QueryResult
 
 
 func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) {
 func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) {
 	return &FakeExecutor{
 	return &FakeExecutor{
@@ -20,9 +20,9 @@ func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) {
 	}, nil
 	}, nil
 }
 }
 
 
-func (e *FakeExecutor) Execute(ctx context.Context, queries QuerySlice, context *QueryContext) *BatchResult {
+func (e *FakeExecutor) Query(ctx context.Context, dsInfo *models.DataSource, context *TsdbQuery) *BatchResult {
 	result := &BatchResult{QueryResults: make(map[string]*QueryResult)}
 	result := &BatchResult{QueryResults: make(map[string]*QueryResult)}
-	for _, query := range queries {
+	for _, query := range context.Queries {
 		if results, has := e.results[query.RefId]; has {
 		if results, has := e.results[query.RefId]; has {
 			result.QueryResults[query.RefId] = results
 			result.QueryResults[query.RefId] = results
 		}
 		}

+ 39 - 25
pkg/tsdb/graphite/graphite.go

@@ -17,24 +17,15 @@ import (
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
+	opentracing "github.com/opentracing/opentracing-go"
 )
 )
 
 
 type GraphiteExecutor struct {
 type GraphiteExecutor struct {
-	*models.DataSource
 	HttpClient *http.Client
 	HttpClient *http.Client
 }
 }
 
 
-func NewGraphiteExecutor(datasource *models.DataSource) (tsdb.Executor, error) {
-	httpClient, err := datasource.GetHttpClient()
-
-	if err != nil {
-		return nil, err
-	}
-
-	return &GraphiteExecutor{
-		DataSource: datasource,
-		HttpClient: httpClient,
-	}, nil
+func NewGraphiteExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
+	return &GraphiteExecutor{}, nil
 }
 }
 
 
 var (
 var (
@@ -43,38 +34,61 @@ var (
 
 
 func init() {
 func init() {
 	glog = log.New("tsdb.graphite")
 	glog = log.New("tsdb.graphite")
-	tsdb.RegisterExecutor("graphite", NewGraphiteExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("graphite", NewGraphiteExecutor)
 }
 }
 
 
-func (e *GraphiteExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{}
 	result := &tsdb.BatchResult{}
 
 
+	from := "-" + formatTimeRange(tsdbQuery.TimeRange.From)
+	until := formatTimeRange(tsdbQuery.TimeRange.To)
+	var target string
+
 	formData := url.Values{
 	formData := url.Values{
-		"from":          []string{"-" + formatTimeRange(context.TimeRange.From)},
-		"until":         []string{formatTimeRange(context.TimeRange.To)},
+		"from":          []string{from},
+		"until":         []string{until},
 		"format":        []string{"json"},
 		"format":        []string{"json"},
 		"maxDataPoints": []string{"500"},
 		"maxDataPoints": []string{"500"},
 	}
 	}
 
 
-	for _, query := range queries {
+	for _, query := range tsdbQuery.Queries {
 		if fullTarget, err := query.Model.Get("targetFull").String(); err == nil {
 		if fullTarget, err := query.Model.Get("targetFull").String(); err == nil {
-			formData["target"] = []string{fixIntervalFormat(fullTarget)}
+			target = fixIntervalFormat(fullTarget)
 		} else {
 		} else {
-			formData["target"] = []string{fixIntervalFormat(query.Model.Get("target").MustString())}
+			target = fixIntervalFormat(query.Model.Get("target").MustString())
 		}
 		}
 	}
 	}
 
 
+	formData["target"] = []string{target}
+
 	if setting.Env == setting.DEV {
 	if setting.Env == setting.DEV {
 		glog.Debug("Graphite request", "params", formData)
 		glog.Debug("Graphite request", "params", formData)
 	}
 	}
 
 
-	req, err := e.createRequest(formData)
+	req, err := e.createRequest(dsInfo, formData)
 	if err != nil {
 	if err != nil {
 		result.Error = err
 		result.Error = err
 		return result
 		return result
 	}
 	}
 
 
-	res, err := ctxhttp.Do(ctx, e.HttpClient, req)
+	httpClient, err := dsInfo.GetHttpClient()
+	if err != nil {
+		result.Error = err
+		return result
+	}
+
+	span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query")
+	span.SetTag("target", target)
+	span.SetTag("from", from)
+	span.SetTag("until", until)
+	defer span.Finish()
+
+	opentracing.GlobalTracer().Inject(
+		span.Context(),
+		opentracing.HTTPHeaders,
+		opentracing.HTTPHeadersCarrier(req.Header))
+
+	res, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
 	if err != nil {
 		result.Error = err
 		result.Error = err
 		return result
 		return result
@@ -126,8 +140,8 @@ func (e *GraphiteExecutor) parseResponse(res *http.Response) ([]TargetResponseDT
 	return data, nil
 	return data, nil
 }
 }
 
 
-func (e *GraphiteExecutor) createRequest(data url.Values) (*http.Request, error) {
-	u, _ := url.Parse(e.Url)
+func (e *GraphiteExecutor) createRequest(dsInfo *models.DataSource, data url.Values) (*http.Request, error) {
+	u, _ := url.Parse(dsInfo.Url)
 	u.Path = path.Join(u.Path, "render")
 	u.Path = path.Join(u.Path, "render")
 
 
 	req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(data.Encode()))
 	req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(data.Encode()))
@@ -137,8 +151,8 @@ func (e *GraphiteExecutor) createRequest(data url.Values) (*http.Request, error)
 	}
 	}
 
 
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
-	if e.BasicAuth {
-		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
+	if dsInfo.BasicAuth {
+		req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.BasicAuthPassword)
 	}
 	}
 
 
 	return req, err
 	return req, err

+ 23 - 26
pkg/tsdb/influxdb/influxdb.go

@@ -17,24 +17,16 @@ import (
 )
 )
 
 
 type InfluxDBExecutor struct {
 type InfluxDBExecutor struct {
-	*models.DataSource
+	//*models.DataSource
 	QueryParser    *InfluxdbQueryParser
 	QueryParser    *InfluxdbQueryParser
 	ResponseParser *ResponseParser
 	ResponseParser *ResponseParser
-	HttpClient     *http.Client
+	//HttpClient     *http.Client
 }
 }
 
 
-func NewInfluxDBExecutor(datasource *models.DataSource) (tsdb.Executor, error) {
-	httpClient, err := datasource.GetHttpClient()
-
-	if err != nil {
-		return nil, err
-	}
-
+func NewInfluxDBExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
 	return &InfluxDBExecutor{
 	return &InfluxDBExecutor{
-		DataSource:     datasource,
 		QueryParser:    &InfluxdbQueryParser{},
 		QueryParser:    &InfluxdbQueryParser{},
 		ResponseParser: &ResponseParser{},
 		ResponseParser: &ResponseParser{},
-		HttpClient:     httpClient,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -44,18 +36,18 @@ var (
 
 
 func init() {
 func init() {
 	glog = log.New("tsdb.influxdb")
 	glog = log.New("tsdb.influxdb")
-	tsdb.RegisterExecutor("influxdb", NewInfluxDBExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor)
 }
 }
 
 
-func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{}
 	result := &tsdb.BatchResult{}
 
 
-	query, err := e.getQuery(queries, context)
+	query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
 
 
-	rawQuery, err := query.Build(context)
+	rawQuery, err := query.Build(tsdbQuery)
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
@@ -64,12 +56,17 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
 		glog.Debug("Influxdb query", "raw query", rawQuery)
 		glog.Debug("Influxdb query", "raw query", rawQuery)
 	}
 	}
 
 
-	req, err := e.createRequest(rawQuery)
+	req, err := e.createRequest(dsInfo, rawQuery)
+	if err != nil {
+		return result.WithError(err)
+	}
+
+	httpClient, err := dsInfo.GetHttpClient()
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
 
 
-	resp, err := ctxhttp.Do(ctx, e.HttpClient, req)
+	resp, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
@@ -98,10 +95,10 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
 	return result
 	return result
 }
 }
 
 
-func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (*Query, error) {
+func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) {
 	for _, v := range queries {
 	for _, v := range queries {
 
 
-		query, err := e.QueryParser.Parse(v.Model, e.DataSource)
+		query, err := e.QueryParser.Parse(v.Model, dsInfo)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -112,8 +109,8 @@ func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.Query
 	return nil, fmt.Errorf("query request contains no queries")
 	return nil, fmt.Errorf("query request contains no queries")
 }
 }
 
 
-func (e *InfluxDBExecutor) createRequest(query string) (*http.Request, error) {
-	u, _ := url.Parse(e.Url)
+func (e *InfluxDBExecutor) createRequest(dsInfo *models.DataSource, query string) (*http.Request, error) {
+	u, _ := url.Parse(dsInfo.Url)
 	u.Path = path.Join(u.Path, "query")
 	u.Path = path.Join(u.Path, "query")
 
 
 	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
 	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
@@ -123,18 +120,18 @@ func (e *InfluxDBExecutor) createRequest(query string) (*http.Request, error) {
 
 
 	params := req.URL.Query()
 	params := req.URL.Query()
 	params.Set("q", query)
 	params.Set("q", query)
-	params.Set("db", e.Database)
+	params.Set("db", dsInfo.Database)
 	params.Set("epoch", "s")
 	params.Set("epoch", "s")
 	req.URL.RawQuery = params.Encode()
 	req.URL.RawQuery = params.Encode()
 
 
 	req.Header.Set("User-Agent", "Grafana")
 	req.Header.Set("User-Agent", "Grafana")
 
 
-	if e.BasicAuth {
-		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
+	if dsInfo.BasicAuth {
+		req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.BasicAuthPassword)
 	}
 	}
 
 
-	if !e.BasicAuth && e.User != "" {
-		req.SetBasicAuth(e.User, e.Password)
+	if !dsInfo.BasicAuth && dsInfo.User != "" {
+		req.SetBasicAuth(dsInfo.User, dsInfo.Password)
 	}
 	}
 
 
 	glog.Debug("Influxdb request", "url", req.URL.String())
 	glog.Debug("Influxdb request", "url", req.URL.String())

+ 5 - 5
pkg/tsdb/influxdb/query.go

@@ -16,7 +16,7 @@ var (
 	regexpMeasurementPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`)
 	regexpMeasurementPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`)
 )
 )
 
 
-func (query *Query) Build(queryContext *tsdb.QueryContext) (string, error) {
+func (query *Query) Build(queryContext *tsdb.TsdbQuery) (string, error) {
 	var res string
 	var res string
 
 
 	if query.UseRawQuery && query.RawQuery != "" {
 	if query.UseRawQuery && query.RawQuery != "" {
@@ -41,7 +41,7 @@ func (query *Query) Build(queryContext *tsdb.QueryContext) (string, error) {
 	return res, nil
 	return res, nil
 }
 }
 
 
-func getDefinedInterval(query *Query, queryContext *tsdb.QueryContext) (*tsdb.Interval, error) {
+func getDefinedInterval(query *Query, queryContext *tsdb.TsdbQuery) (*tsdb.Interval, error) {
 	defaultInterval := tsdb.CalculateInterval(queryContext.TimeRange)
 	defaultInterval := tsdb.CalculateInterval(queryContext.TimeRange)
 
 
 	if query.Interval == "" {
 	if query.Interval == "" {
@@ -104,7 +104,7 @@ func (query *Query) renderTags() []string {
 	return res
 	return res
 }
 }
 
 
-func (query *Query) renderTimeFilter(queryContext *tsdb.QueryContext) string {
+func (query *Query) renderTimeFilter(queryContext *tsdb.TsdbQuery) string {
 	from := "now() - " + queryContext.TimeRange.From
 	from := "now() - " + queryContext.TimeRange.From
 	to := ""
 	to := ""
 
 
@@ -115,7 +115,7 @@ func (query *Query) renderTimeFilter(queryContext *tsdb.QueryContext) string {
 	return fmt.Sprintf("time > %s%s", from, to)
 	return fmt.Sprintf("time > %s%s", from, to)
 }
 }
 
 
-func (query *Query) renderSelectors(queryContext *tsdb.QueryContext) string {
+func (query *Query) renderSelectors(queryContext *tsdb.TsdbQuery) string {
 	res := "SELECT "
 	res := "SELECT "
 
 
 	var selectors []string
 	var selectors []string
@@ -163,7 +163,7 @@ func (query *Query) renderWhereClause() string {
 	return res
 	return res
 }
 }
 
 
-func (query *Query) renderGroupBy(queryContext *tsdb.QueryContext) string {
+func (query *Query) renderGroupBy(queryContext *tsdb.TsdbQuery) string {
 	groupBy := ""
 	groupBy := ""
 	for i, group := range query.GroupBy {
 	for i, group := range query.GroupBy {
 		if i == 0 {
 		if i == 0 {

+ 7 - 7
pkg/tsdb/influxdb/query_part.go

@@ -15,7 +15,7 @@ type DefinitionParameters struct {
 }
 }
 
 
 type QueryDefinition struct {
 type QueryDefinition struct {
-	Renderer func(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string
+	Renderer func(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string
 	Params   []DefinitionParameters
 	Params   []DefinitionParameters
 }
 }
 
 
@@ -94,14 +94,14 @@ func init() {
 	renders["alias"] = QueryDefinition{Renderer: aliasRenderer}
 	renders["alias"] = QueryDefinition{Renderer: aliasRenderer}
 }
 }
 
 
-func fieldRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
+func fieldRenderer(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string {
 	if part.Params[0] == "*" {
 	if part.Params[0] == "*" {
 		return "*"
 		return "*"
 	}
 	}
 	return fmt.Sprintf(`"%s"`, part.Params[0])
 	return fmt.Sprintf(`"%s"`, part.Params[0])
 }
 }
 
 
-func functionRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
+func functionRenderer(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string {
 	for i, param := range part.Params {
 	for i, param := range part.Params {
 		if part.Type == "time" && param == "auto" {
 		if part.Type == "time" && param == "auto" {
 			part.Params[i] = "$__interval"
 			part.Params[i] = "$__interval"
@@ -117,15 +117,15 @@ func functionRenderer(query *Query, queryContext *tsdb.QueryContext, part *Query
 	return fmt.Sprintf("%s(%s)", part.Type, params)
 	return fmt.Sprintf("%s(%s)", part.Type, params)
 }
 }
 
 
-func suffixRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
+func suffixRenderer(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string {
 	return fmt.Sprintf("%s %s", innerExpr, part.Params[0])
 	return fmt.Sprintf("%s %s", innerExpr, part.Params[0])
 }
 }
 
 
-func aliasRenderer(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
+func aliasRenderer(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string {
 	return fmt.Sprintf(`%s AS "%s"`, innerExpr, part.Params[0])
 	return fmt.Sprintf(`%s AS "%s"`, innerExpr, part.Params[0])
 }
 }
 
 
-func (r QueryDefinition) Render(query *Query, queryContext *tsdb.QueryContext, part *QueryPart, innerExpr string) string {
+func (r QueryDefinition) Render(query *Query, queryContext *tsdb.TsdbQuery, part *QueryPart, innerExpr string) string {
 	return r.Renderer(query, queryContext, part, innerExpr)
 	return r.Renderer(query, queryContext, part, innerExpr)
 }
 }
 
 
@@ -149,6 +149,6 @@ type QueryPart struct {
 	Params []string
 	Params []string
 }
 }
 
 
-func (qp *QueryPart) Render(query *Query, queryContext *tsdb.QueryContext, expr string) string {
+func (qp *QueryPart) Render(query *Query, queryContext *tsdb.TsdbQuery, expr string) string {
 	return qp.Def.Renderer(query, queryContext, qp, expr)
 	return qp.Def.Renderer(query, queryContext, qp, expr)
 }
 }

+ 1 - 1
pkg/tsdb/influxdb/query_part_test.go

@@ -10,7 +10,7 @@ import (
 func TestInfluxdbQueryPart(t *testing.T) {
 func TestInfluxdbQueryPart(t *testing.T) {
 	Convey("Influxdb query parts", t, func() {
 	Convey("Influxdb query parts", t, func() {
 
 
-		queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("5m", "now")}
+		queryContext := &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("5m", "now")}
 		query := &Query{}
 		query := &Query{}
 
 
 		Convey("render field ", func() {
 		Convey("render field ", func() {

+ 3 - 3
pkg/tsdb/influxdb/query_test.go

@@ -28,7 +28,7 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
 		tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="}
 		tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="}
 		tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"}
 		tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"}
 
 
-		queryContext := &tsdb.QueryContext{
+		queryContext := &tsdb.TsdbQuery{
 			TimeRange: tsdb.NewTimeRange("5m", "now"),
 			TimeRange: tsdb.NewTimeRange("5m", "now"),
 		}
 		}
 
 
@@ -101,12 +101,12 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
 			query := Query{}
 			query := Query{}
 			Convey("render from: 2h to now-1h", func() {
 			Convey("render from: 2h to now-1h", func() {
 				query := Query{}
 				query := Query{}
-				queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("2h", "now-1h")}
+				queryContext := &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("2h", "now-1h")}
 				So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h")
 				So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h")
 			})
 			})
 
 
 			Convey("render from: 10m", func() {
 			Convey("render from: 10m", func() {
-				queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("10m", "now")}
+				queryContext := &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("10m", "now")}
 				So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 10m")
 				So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 10m")
 			})
 			})
 		})
 		})

+ 5 - 8
pkg/tsdb/models.go

@@ -6,24 +6,21 @@ import (
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 )
 )
 
 
+type TsdbQuery struct {
+	TimeRange *TimeRange
+	Queries   []*Query
+}
+
 type Query struct {
 type Query struct {
 	RefId         string
 	RefId         string
 	Model         *simplejson.Json
 	Model         *simplejson.Json
 	Depends       []string
 	Depends       []string
 	DataSource    *models.DataSource
 	DataSource    *models.DataSource
 	Results       []*TimeSeries
 	Results       []*TimeSeries
-	Exclude       bool
 	MaxDataPoints int64
 	MaxDataPoints int64
 	IntervalMs    int64
 	IntervalMs    int64
 }
 }
 
 
-type QuerySlice []*Query
-
-type Request struct {
-	TimeRange *TimeRange
-	Queries   QuerySlice
-}
-
 type Response struct {
 type Response struct {
 	BatchTimings []*BatchTiming          `json:"timings"`
 	BatchTimings []*BatchTiming          `json:"timings"`
 	Results      map[string]*QueryResult `json:"results"`
 	Results      map[string]*QueryResult `json:"results"`

+ 0 - 129
pkg/tsdb/mqe/httpClient.go

@@ -1,129 +0,0 @@
-package mqe
-
-import (
-	"context"
-	"net/http"
-	"net/url"
-	"path"
-	"strings"
-
-	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/setting"
-	"github.com/grafana/grafana/pkg/tsdb"
-
-	"golang.org/x/net/context/ctxhttp"
-)
-
-var (
-	MaxWorker int = 4
-)
-
-type apiClient struct {
-	*models.DataSource
-	log            log.Logger
-	httpClient     *http.Client
-	responseParser *ResponseParser
-}
-
-func NewApiClient(httpClient *http.Client, datasource *models.DataSource) *apiClient {
-	return &apiClient{
-		DataSource:     datasource,
-		log:            log.New("tsdb.mqe"),
-		httpClient:     httpClient,
-		responseParser: NewResponseParser(),
-	}
-}
-
-func (e *apiClient) PerformRequests(ctx context.Context, queries []QueryToSend) (*tsdb.QueryResult, error) {
-	queryResult := &tsdb.QueryResult{}
-
-	queryCount := len(queries)
-	jobsChan := make(chan QueryToSend, queryCount)
-	resultChan := make(chan []*tsdb.TimeSeries, queryCount)
-	errorsChan := make(chan error, 1)
-	for w := 1; w <= MaxWorker; w++ {
-		go e.spawnWorker(ctx, w, jobsChan, resultChan, errorsChan)
-	}
-
-	for _, v := range queries {
-		jobsChan <- v
-	}
-	close(jobsChan)
-
-	resultCounter := 0
-	for {
-		select {
-		case timeseries := <-resultChan:
-			queryResult.Series = append(queryResult.Series, timeseries...)
-			resultCounter++
-
-			if resultCounter == queryCount {
-				close(resultChan)
-				return queryResult, nil
-			}
-		case err := <-errorsChan:
-			return nil, err
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		}
-	}
-}
-
-func (e *apiClient) spawnWorker(ctx context.Context, id int, jobs chan QueryToSend, results chan []*tsdb.TimeSeries, errors chan error) {
-	e.log.Debug("Spawning worker", "id", id)
-	for query := range jobs {
-		if setting.Env == setting.DEV {
-			e.log.Debug("Sending request", "query", query.RawQuery)
-		}
-
-		req, err := e.createRequest(query.RawQuery)
-
-		resp, err := ctxhttp.Do(ctx, e.httpClient, req)
-		if err != nil {
-			errors <- err
-			return
-		}
-
-		series, err := e.responseParser.Parse(resp, query)
-		if err != nil {
-			errors <- err
-			return
-		}
-
-		results <- series
-	}
-	e.log.Debug("Worker is complete", "id", id)
-}
-
-func (e *apiClient) createRequest(query string) (*http.Request, error) {
-	u, err := url.Parse(e.Url)
-	if err != nil {
-		return nil, err
-	}
-
-	u.Path = path.Join(u.Path, "query")
-
-	payload := simplejson.New()
-	payload.Set("query", query)
-
-	jsonPayload, err := payload.MarshalJSON()
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(string(jsonPayload)))
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Set("User-Agent", "Grafana")
-	req.Header.Set("Content-Type", "application/json")
-
-	if e.BasicAuth {
-		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
-	}
-
-	return req, nil
-}

+ 0 - 60
pkg/tsdb/mqe/model_parser.go

@@ -1,60 +0,0 @@
-package mqe
-
-import (
-	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/tsdb"
-)
-
-func NewQueryParser() *QueryParser {
-	return &QueryParser{}
-}
-
-type QueryParser struct{}
-
-func (qp *QueryParser) Parse(model *simplejson.Json, dsInfo *models.DataSource, queryContext *tsdb.QueryContext) (*Query, error) {
-	query := &Query{TimeRange: queryContext.TimeRange}
-	query.AddClusterToAlias = model.Get("addClusterToAlias").MustBool(false)
-	query.AddHostToAlias = model.Get("addHostToAlias").MustBool(false)
-	query.UseRawQuery = model.Get("rawQuery").MustBool(false)
-	query.RawQuery = model.Get("query").MustString("")
-
-	query.Cluster = model.Get("cluster").MustStringArray([]string{})
-	query.Hosts = model.Get("hosts").MustStringArray([]string{})
-
-	var metrics []Metric
-	var err error
-	for _, metricsObj := range model.Get("metrics").MustArray() {
-		metricJson := simplejson.NewFromAny(metricsObj)
-		var m Metric
-
-		m.Alias = metricJson.Get("alias").MustString("")
-		m.Metric, err = metricJson.Get("metric").String()
-		if err != nil {
-			return nil, err
-		}
-
-		metrics = append(metrics, m)
-	}
-
-	query.Metrics = metrics
-
-	var functions []Function
-	for _, functionListObj := range model.Get("functionList").MustArray() {
-		functionListJson := simplejson.NewFromAny(functionListObj)
-		var f Function
-
-		f.Func = functionListJson.Get("func").MustString("")
-		if err != nil {
-			return nil, err
-		}
-
-		if f.Func != "" {
-			functions = append(functions, f)
-		}
-	}
-
-	query.FunctionList = functions
-
-	return query, nil
-}

+ 0 - 127
pkg/tsdb/mqe/model_parser_test.go

@@ -1,127 +0,0 @@
-package mqe
-
-import (
-	"testing"
-
-	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/tsdb"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestMQEQueryParser(t *testing.T) {
-	Convey("MQE query parser", t, func() {
-		parser := &QueryParser{}
-
-		dsInfo := &models.DataSource{JsonData: simplejson.New()}
-		queryContext := &tsdb.QueryContext{}
-
-		Convey("can parse simple mqe model", func() {
-			json := `
-      {
-        "cluster": [],
-        "hosts": [
-          "staples-lab-1"
-        ],
-        "metrics": [
-          {
-            "metric": "os.cpu.all*"
-          }
-        ],
-        "rawQuery": "",
-        "refId": "A"
-      }
-      `
-			modelJson, err := simplejson.NewJson([]byte(json))
-			So(err, ShouldBeNil)
-
-			query, err := parser.Parse(modelJson, dsInfo, queryContext)
-			So(err, ShouldBeNil)
-			So(query.UseRawQuery, ShouldBeFalse)
-
-			So(len(query.Cluster), ShouldEqual, 0)
-			So(query.Hosts[0], ShouldEqual, "staples-lab-1")
-			So(query.Metrics[0].Metric, ShouldEqual, "os.cpu.all*")
-		})
-
-		Convey("can parse multi serie mqe model", func() {
-			json := `
-      {
-        "cluster": [
-          "demoapp"
-        ],
-        "hosts": [
-          "staples-lab-1"
-        ],
-        "metrics": [
-          {
-            "metric": "os.cpu.all.active_percentage"
-          },
-          {
-            "metric": "os.disk.sda.io_time"
-          }
-        ],
-        "functionList": [
-          {
-            "func": "aggregate.min"
-          },
-          {
-             "func": "aggregate.max"
-          }
-        ],
-        "rawQuery": "",
-        "refId": "A",
-        "addClusterToAlias": true,
-        "addHostToAlias": true
-      }
-      `
-			modelJson, err := simplejson.NewJson([]byte(json))
-			So(err, ShouldBeNil)
-
-			query, err := parser.Parse(modelJson, dsInfo, queryContext)
-			So(err, ShouldBeNil)
-			So(query.UseRawQuery, ShouldBeFalse)
-			So(query.Cluster[0], ShouldEqual, "demoapp")
-			So(query.Metrics[0].Metric, ShouldEqual, "os.cpu.all.active_percentage")
-			So(query.Metrics[1].Metric, ShouldEqual, "os.disk.sda.io_time")
-			So(query.FunctionList[0].Func, ShouldEqual, "aggregate.min")
-			So(query.FunctionList[1].Func, ShouldEqual, "aggregate.max")
-		})
-
-		Convey("can parse raw query", func() {
-			json := `
-      {
-        "addClusterToAlias": true,
-        "addHostToAlias": true,
-        "cluster": [],
-        "hosts": [
-          "staples-lab-1"
-        ],
-        "metrics": [
-          {
-            "alias": "cpu active",
-            "metric": "os.cpu.all.active_percentage"
-          },
-          {
-            "alias": "disk sda time",
-            "metric": "os.disk.sda.io_time"
-          }
-        ],
-        "rawQuery": true,
-        "query": "raw-query",
-        "refId": "A"
-      }
-      `
-			modelJson, err := simplejson.NewJson([]byte(json))
-			So(err, ShouldBeNil)
-
-			query, err := parser.Parse(modelJson, dsInfo, queryContext)
-			So(err, ShouldBeNil)
-
-			So(query.UseRawQuery, ShouldBeTrue)
-			So(query.RawQuery, ShouldEqual, "raw-query")
-			So(query.AddClusterToAlias, ShouldBeTrue)
-			So(query.AddHostToAlias, ShouldBeTrue)
-		})
-	})
-}

+ 0 - 85
pkg/tsdb/mqe/mqe.go

@@ -1,85 +0,0 @@
-package mqe
-
-import (
-	"context"
-	"net/http"
-
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/tsdb"
-)
-
-type MQEExecutor struct {
-	*models.DataSource
-	queryParser *QueryParser
-	apiClient   *apiClient
-	httpClient  *http.Client
-	log         log.Logger
-	tokenClient *TokenClient
-}
-
-func NewMQEExecutor(dsInfo *models.DataSource) (tsdb.Executor, error) {
-	httpclient, err := dsInfo.GetHttpClient()
-	if err != nil {
-		return nil, err
-	}
-
-	return &MQEExecutor{
-		DataSource:  dsInfo,
-		httpClient:  httpclient,
-		log:         log.New("tsdb.mqe"),
-		queryParser: NewQueryParser(),
-		apiClient:   NewApiClient(httpclient, dsInfo),
-		tokenClient: NewTokenClient(dsInfo),
-	}, nil
-}
-
-func init() {
-	tsdb.RegisterExecutor("mqe-datasource", NewMQEExecutor)
-}
-
-type QueryToSend struct {
-	RawQuery string
-	Metric   Metric
-	QueryRef *Query
-}
-
-func (e *MQEExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
-
-	availableSeries, err := e.tokenClient.GetTokenData(ctx)
-	if err != nil {
-		return result.WithError(err)
-	}
-
-	var mqeQueries []*Query
-	for _, v := range queries {
-		q, err := e.queryParser.Parse(v.Model, e.DataSource, queryContext)
-		if err != nil {
-			return result.WithError(err)
-		}
-		mqeQueries = append(mqeQueries, q)
-	}
-
-	var rawQueries []QueryToSend
-	for _, v := range mqeQueries {
-		queries, err := v.Build(availableSeries.Metrics)
-		if err != nil {
-			return result.WithError(err)
-		}
-
-		rawQueries = append(rawQueries, queries...)
-	}
-
-	e.log.Debug("Sending request", "url", e.DataSource.Url)
-
-	queryResult, err := e.apiClient.PerformRequests(ctx, rawQueries)
-	if err != nil {
-		return result.WithError(err)
-	}
-
-	result.QueryResults = make(map[string]*tsdb.QueryResult)
-	result.QueryResults["A"] = queryResult
-
-	return result
-}

+ 0 - 177
pkg/tsdb/mqe/response_parser.go

@@ -1,177 +0,0 @@
-package mqe
-
-import (
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"strconv"
-	"strings"
-
-	"fmt"
-
-	"regexp"
-
-	"github.com/grafana/grafana/pkg/components/null"
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/tsdb"
-)
-
-func NewResponseParser() *ResponseParser {
-	return &ResponseParser{
-		log: log.New("tsdb.mqe"),
-	}
-}
-
-var (
-	indexAliasPattern    *regexp.Regexp
-	wildcardAliasPattern *regexp.Regexp
-)
-
-func init() {
-	indexAliasPattern = regexp.MustCompile(`\$(\d)`)
-	wildcardAliasPattern = regexp.MustCompile(`[*!]`)
-}
-
-type MQEResponse struct {
-	Success bool               `json:"success"`
-	Name    string             `json:"name"`
-	Body    []MQEResponseSerie `json:"body"`
-}
-
-type ResponseTimeRange struct {
-	Start      int64 `json:"start"`
-	End        int64 `json:"end"`
-	Resolution int64 `json:"Resolution"`
-}
-
-type MQEResponseSerie struct {
-	Query     string            `json:"query"`
-	Name      string            `json:"name"`
-	Type      string            `json:"type"`
-	Series    []MQESerie        `json:"series"`
-	TimeRange ResponseTimeRange `json:"timerange"`
-}
-
-type MQESerie struct {
-	Values []null.Float      `json:"values"`
-	Tagset map[string]string `json:"tagset"`
-}
-
-type ResponseParser struct {
-	log log.Logger
-}
-
-func (parser *ResponseParser) Parse(res *http.Response, queryRef QueryToSend) ([]*tsdb.TimeSeries, error) {
-	body, err := ioutil.ReadAll(res.Body)
-	defer res.Body.Close()
-	if err != nil {
-		return nil, err
-	}
-
-	if res.StatusCode/100 != 2 {
-		parser.log.Error("Request failed", "status code", res.StatusCode, "body", string(body))
-		return nil, fmt.Errorf("Returned invalid statuscode")
-	}
-
-	var data *MQEResponse = &MQEResponse{}
-	err = json.Unmarshal(body, data)
-	if err != nil {
-		parser.log.Info("Failed to unmarshal response", "error", err, "status", res.Status, "body", string(body))
-		return nil, err
-	}
-
-	if !data.Success {
-		return nil, fmt.Errorf("Request failed.")
-	}
-
-	var series []*tsdb.TimeSeries
-	for _, body := range data.Body {
-		for _, mqeSerie := range body.Series {
-			serie := &tsdb.TimeSeries{
-				Tags: map[string]string{},
-				Name: parser.formatLegend(body, mqeSerie, queryRef),
-			}
-			for key, value := range mqeSerie.Tagset {
-				serie.Tags[key] = value
-			}
-
-			for i, value := range mqeSerie.Values {
-				timestamp := body.TimeRange.Start + int64(i)*body.TimeRange.Resolution
-				serie.Points = append(serie.Points, tsdb.NewTimePoint(value, float64(timestamp)))
-			}
-
-			series = append(series, serie)
-		}
-	}
-
-	return series, nil
-}
-
-func (parser *ResponseParser) formatLegend(body MQEResponseSerie, mqeSerie MQESerie, queryToSend QueryToSend) string {
-	namePrefix := ""
-
-	//append predefined tags to seriename
-	for key, value := range mqeSerie.Tagset {
-		if key == "cluster" && queryToSend.QueryRef.AddClusterToAlias {
-			namePrefix += value + " "
-		}
-	}
-	for key, value := range mqeSerie.Tagset {
-		if key == "host" && queryToSend.QueryRef.AddHostToAlias {
-			namePrefix += value + " "
-		}
-	}
-
-	return namePrefix + parser.formatName(body, queryToSend)
-}
-
-func (parser *ResponseParser) formatName(body MQEResponseSerie, queryToSend QueryToSend) string {
-	if indexAliasPattern.MatchString(queryToSend.Metric.Alias) {
-		return parser.indexAlias(body, queryToSend)
-	}
-
-	if wildcardAliasPattern.MatchString(queryToSend.Metric.Metric) && wildcardAliasPattern.MatchString(queryToSend.Metric.Alias) {
-		return parser.wildcardAlias(body, queryToSend)
-	}
-
-	return body.Name
-}
-
-func (parser *ResponseParser) wildcardAlias(body MQEResponseSerie, queryToSend QueryToSend) string {
-	regString := strings.Replace(queryToSend.Metric.Metric, `*`, `(.*)`, 1)
-	reg, err := regexp.Compile(regString)
-	if err != nil {
-		return queryToSend.Metric.Alias
-	}
-
-	matches := reg.FindAllStringSubmatch(queryToSend.RawQuery, -1)
-
-	if len(matches) == 0 || len(matches[0]) < 2 {
-		return queryToSend.Metric.Alias
-	}
-
-	return matches[0][1]
-}
-
-func (parser *ResponseParser) indexAlias(body MQEResponseSerie, queryToSend QueryToSend) string {
-	queryNameParts := strings.Split(queryToSend.Metric.Metric, `.`)
-
-	name := indexAliasPattern.ReplaceAllStringFunc(queryToSend.Metric.Alias, func(in string) string {
-		positionName := strings.TrimSpace(strings.Replace(in, "$", "", 1))
-
-		pos, err := strconv.Atoi(positionName)
-		if err != nil {
-			return ""
-		}
-
-		for i, part := range queryNameParts {
-			if i == pos-1 {
-				return strings.TrimSpace(part)
-			}
-		}
-
-		return ""
-	})
-
-	return strings.Replace(name, " ", ".", -1)
-}

+ 0 - 187
pkg/tsdb/mqe/response_parser_test.go

@@ -1,187 +0,0 @@
-package mqe
-
-import (
-	"testing"
-
-	"net/http"
-	"strings"
-
-	"io/ioutil"
-
-	"github.com/grafana/grafana/pkg/components/null"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-var (
-	testJson string
-)
-
-func TestMQEResponseParser(t *testing.T) {
-	Convey("MQE response parser", t, func() {
-		parser := NewResponseParser()
-
-		Convey("Can parse response", func() {
-			queryRef := QueryToSend{
-				QueryRef: &Query{
-					AddClusterToAlias: true,
-					AddHostToAlias:    true,
-				},
-				Metric: Metric{Alias: ""},
-			}
-
-			response := &http.Response{
-				StatusCode: 200,
-				Body:       ioutil.NopCloser(strings.NewReader(testJson)),
-			}
-			res, err := parser.Parse(response, queryRef)
-			So(err, ShouldBeNil)
-			So(len(res), ShouldEqual, 2)
-			So(len(res[0].Points), ShouldEqual, 14)
-			So(res[0].Name, ShouldEqual, "demoapp staples-lab-1 os.disk.sda3.weighted_io_time")
-			startTime := 1479287280000
-			for i := 0; i < 11; i++ {
-				So(res[0].Points[i][0].Float64, ShouldEqual, i+1)
-				So(res[0].Points[i][1].Float64, ShouldEqual, startTime+(i*30000))
-			}
-
-		})
-
-		Convey("Can format legend", func() {
-			mqeSerie := MQESerie{
-				Tagset: map[string]string{
-					"cluster": "demoapp",
-					"host":    "staples-lab-1",
-				},
-				Values: []null.Float{null.NewFloat(3, true)},
-			}
-
-			Convey("with empty alias", func() {
-				serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time"}
-				queryRef := QueryToSend{
-					QueryRef: &Query{
-						AddClusterToAlias: true,
-						AddHostToAlias:    true,
-					},
-					Metric: Metric{Alias: ""},
-				}
-				legend := parser.formatLegend(serie, mqeSerie, queryRef)
-				So(legend, ShouldEqual, "demoapp staples-lab-1 os.disk.sda3.weighted_io_time")
-			})
-
-			Convey("with index alias (ex $2 $3)", func() {
-				serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time"}
-				queryRef := QueryToSend{
-					QueryRef: &Query{
-						AddClusterToAlias: true,
-						AddHostToAlias:    true,
-					},
-					Metric: Metric{Alias: "$2 $3", Metric: "os.disk.sda3.weighted_io_time"},
-				}
-				legend := parser.formatLegend(serie, mqeSerie, queryRef)
-				So(legend, ShouldEqual, "demoapp staples-lab-1 disk.sda3")
-			})
-
-			Convey("with wildcard alias", func() {
-				serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time", Query: "os.disk.*"}
-
-				queryRef := QueryToSend{
-					QueryRef: &Query{
-						AddClusterToAlias: true,
-						AddHostToAlias:    true,
-					},
-					RawQuery: "os.disk.sda3.weighted_io_time",
-					Metric:   Metric{Alias: "*", Metric: "os.disk.*.weighted_io_time"},
-				}
-				legend := parser.formatLegend(serie, mqeSerie, queryRef)
-				So(legend, ShouldEqual, "demoapp staples-lab-1 sda3")
-			})
-		})
-	})
-}
-
-func init() {
-	testJson = `{
-    "success": true,
-    "name": "select",
-    "body": [
-      {
-        "query": "os.disk.sda3.weighted_io_time",
-        "name": "os.disk.sda3.weighted_io_time",
-        "type": "series",
-        "series": [
-          {
-            "tagset": {
-              "cluster": "demoapp",
-              "host": "staples-lab-1"
-            },
-            "values": [1,2,3,4,5,6,7,8,9,10,11, null, null, null]
-          },
-          {
-            "tagset": {
-              "cluster": "demoapp",
-              "host": "staples-lab-2"
-            },
-            "values": [11,10,9,8,7,6,5,4,3,2,1]
-          }
-        ],
-        "timerange": {
-          "start": 1479287280000,
-          "end": 1479287580000,
-          "resolution": 30000
-        }
-      }
-    ],
-    "metadata": {
-      "description": {
-        "cluster": [
-          "demoapp"
-        ],
-        "host": [
-          "staples-lab-1",
-          "staples-lab-2"
-        ]
-      },
-      "notes": null,
-      "profile": [
-        {
-          "name": "Parsing Query",
-          "start": "2016-11-16T04:16:21.874354721-05:00",
-          "finish": "2016-11-16T04:16:21.874762291-05:00"
-        },
-        {
-          "name": "Cassandra GetAllTags",
-          "start": "2016-11-16T04:16:21.874907171-05:00",
-          "finish": "2016-11-16T04:16:21.876401922-05:00"
-        },
-        {
-          "name": "CachedMetricMetadataAPI_GetAllTags_Expired",
-          "start": "2016-11-16T04:16:21.874904751-05:00",
-          "finish": "2016-11-16T04:16:21.876407852-05:00"
-        },
-        {
-          "name": "CachedMetricMetadataAPI_GetAllTags",
-          "start": "2016-11-16T04:16:21.874899491-05:00",
-          "finish": "2016-11-16T04:16:21.876410382-05:00"
-        },
-        {
-          "name": "Blueflood FetchSingleTimeseries Resolution",
-          "description": "os.disk.sda3.weighted_io_time [app=demoapp,host=staples-lab-1]\n at 30s",
-          "start": "2016-11-16T04:16:21.876623312-05:00",
-          "finish": "2016-11-16T04:16:21.881763444-05:00"
-        },
-        {
-          "name": "Blueflood FetchSingleTimeseries Resolution",
-          "description": "os.disk.sda3.weighted_io_time [app=demoapp,host=staples-lab-2]\n at 30s",
-          "start": "2016-11-16T04:16:21.876642682-05:00",
-          "finish": "2016-11-16T04:16:21.881895914-05:00"
-        },
-        {
-          "name": "Blueflood FetchMultipleTimeseries",
-          "start": "2016-11-16T04:16:21.876418022-05:00",
-          "finish": "2016-11-16T04:16:21.881921474-05:00"
-        }
-      ]
-    }
-  }
-  `
-}

+ 0 - 101
pkg/tsdb/mqe/token_client.go

@@ -1,101 +0,0 @@
-package mqe
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"path"
-	"time"
-
-	"golang.org/x/net/context/ctxhttp"
-
-	"strconv"
-
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/patrickmn/go-cache"
-)
-
-var tokenCache *cache.Cache
-
-func init() {
-	tokenCache = cache.New(5*time.Minute, 30*time.Second)
-}
-
-type TokenClient struct {
-	log        log.Logger
-	Datasource *models.DataSource
-	HttpClient *http.Client
-}
-
-func NewTokenClient(datasource *models.DataSource) *TokenClient {
-	httpClient, _ := datasource.GetHttpClient()
-
-	return &TokenClient{
-		log:        log.New("tsdb.mqe.tokenclient"),
-		Datasource: datasource,
-		HttpClient: httpClient,
-	}
-}
-
-func (client *TokenClient) GetTokenData(ctx context.Context) (*TokenBody, error) {
-	key := strconv.FormatInt(client.Datasource.Id, 10)
-
-	item, found := tokenCache.Get(key)
-	if found {
-		if result, ok := item.(*TokenBody); ok {
-			return result, nil
-		}
-	}
-
-	b, err := client.RequestTokenData(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	tokenCache.Set(key, b, cache.DefaultExpiration)
-
-	return b, nil
-}
-
-func (client *TokenClient) RequestTokenData(ctx context.Context) (*TokenBody, error) {
-	u, _ := url.Parse(client.Datasource.Url)
-	u.Path = path.Join(u.Path, "token")
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		client.log.Info("Failed to create request", "error", err)
-	}
-
-	res, err := ctxhttp.Do(ctx, client.HttpClient, req)
-	if err != nil {
-		return nil, err
-	}
-
-	body, err := ioutil.ReadAll(res.Body)
-	defer res.Body.Close()
-	if err != nil {
-		return nil, err
-	}
-
-	if res.StatusCode/100 != 2 {
-		client.log.Info("Request failed", "status", res.Status, "body", string(body))
-		return nil, fmt.Errorf("Request failed status: %v", res.Status)
-	}
-
-	var result *TokenResponse
-	err = json.Unmarshal(body, &result)
-	if err != nil {
-		client.log.Info("Failed to unmarshal response", "error", err, "status", res.Status, "body", string(body))
-		return nil, err
-	}
-
-	if !result.Success {
-		return nil, fmt.Errorf("Request failed for unknown reason.")
-	}
-
-	return &result.Body, nil
-}

+ 0 - 27
pkg/tsdb/mqe/token_client_test.go

@@ -1,27 +0,0 @@
-package mqe
-
-import (
-	"context"
-	"testing"
-
-	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/models"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestTokenClient(t *testing.T) {
-	SkipConvey("Token client", t, func() {
-		dsInfo := &models.DataSource{
-			JsonData: simplejson.New(),
-			Url:      "",
-		}
-
-		client := NewTokenClient(dsInfo)
-
-		body, err := client.RequestTokenData(context.TODO())
-
-		So(err, ShouldBeNil)
-		//So(len(body.Functions), ShouldBeGreaterThan, 1)
-		So(len(body.Metrics), ShouldBeGreaterThan, 1)
-	})
-}

+ 0 - 137
pkg/tsdb/mqe/types.go

@@ -1,137 +0,0 @@
-package mqe
-
-import (
-	"fmt"
-
-	"strings"
-
-	"regexp"
-
-	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/tsdb"
-)
-
-type Metric struct {
-	Metric string
-	Alias  string
-}
-
-type Function struct {
-	Func string
-}
-
-type Query struct {
-	Metrics           []Metric
-	Hosts             []string
-	Cluster           []string
-	FunctionList      []Function
-	AddClusterToAlias bool
-	AddHostToAlias    bool
-
-	TimeRange   *tsdb.TimeRange
-	UseRawQuery bool
-	RawQuery    string
-}
-
-var (
-	containsWildcardPattern *regexp.Regexp = regexp.MustCompile(`\*`)
-)
-
-func (q *Query) Build(availableSeries []string) ([]QueryToSend, error) {
-	var queriesToSend []QueryToSend
-	where := q.buildWhereClause()
-	functions := q.buildFunctionList()
-
-	for _, metric := range q.Metrics {
-		alias := ""
-		if metric.Alias != "" {
-			alias = fmt.Sprintf(" {%s}", metric.Alias)
-		}
-
-		if !containsWildcardPattern.Match([]byte(metric.Metric)) {
-			rawQuery := q.renderQuerystring(metric.Metric, functions, alias, where, q.TimeRange)
-			queriesToSend = append(queriesToSend, QueryToSend{
-				RawQuery: rawQuery,
-				QueryRef: q,
-				Metric:   metric,
-			})
-		} else {
-			m := strings.Replace(metric.Metric, "*", ".*", -1)
-			mp, err := regexp.Compile(m)
-
-			if err != nil {
-				log.Error2("failed to compile regex for ", "metric", m)
-				continue
-			}
-
-			//TODO: this lookup should be cached
-			for _, wildcardMatch := range availableSeries {
-				if mp.Match([]byte(wildcardMatch)) {
-					rawQuery := q.renderQuerystring(wildcardMatch, functions, alias, where, q.TimeRange)
-					queriesToSend = append(queriesToSend, QueryToSend{
-						RawQuery: rawQuery,
-						QueryRef: q,
-						Metric:   metric,
-					})
-				}
-			}
-		}
-	}
-
-	return queriesToSend, nil
-}
-
-func (q *Query) renderQuerystring(path, functions, alias, where string, timerange *tsdb.TimeRange) string {
-	return fmt.Sprintf(
-		"`%s`%s%s %s from %v to %v",
-		path,
-		functions,
-		alias,
-		where,
-		q.TimeRange.GetFromAsMsEpoch(),
-		q.TimeRange.GetToAsMsEpoch())
-}
-
-func (q *Query) buildFunctionList() string {
-	functions := ""
-	for _, v := range q.FunctionList {
-		functions = fmt.Sprintf("%s|%s", functions, v.Func)
-	}
-
-	return functions
-}
-
-func (q *Query) buildWhereClause() string {
-	hasApps := len(q.Cluster) > 0
-	hasHosts := len(q.Hosts) > 0
-
-	where := ""
-	if hasHosts || hasApps {
-		where += "where "
-	}
-
-	if hasApps {
-		apps := strings.Join(q.Cluster, "', '")
-		where += fmt.Sprintf("cluster in ('%s')", apps)
-	}
-
-	if hasHosts && hasApps {
-		where += " and "
-	}
-
-	if hasHosts {
-		hosts := strings.Join(q.Hosts, "', '")
-		where += fmt.Sprintf("host in ('%s')", hosts)
-	}
-
-	return where
-}
-
-type TokenBody struct {
-	Metrics []string
-}
-
-type TokenResponse struct {
-	Success bool
-	Body    TokenBody
-}

+ 0 - 95
pkg/tsdb/mqe/types_test.go

@@ -1,95 +0,0 @@
-package mqe
-
-import (
-	"testing"
-
-	"time"
-
-	"fmt"
-
-	"github.com/grafana/grafana/pkg/tsdb"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestWildcardExpansion(t *testing.T) {
-	availableMetrics := []string{
-		"os.cpu.all.idle",
-		"os.cpu.1.idle",
-		"os.cpu.2.idle",
-		"os.cpu.3.idle",
-	}
-
-	now := time.Now()
-	from := now.Add((time.Minute*5)*-1).UnixNano() / int64(time.Millisecond)
-	to := now.UnixNano() / int64(time.Millisecond)
-
-	Convey("Can expanding query", t, func() {
-		Convey("Without wildcard series", func() {
-			query := &Query{
-				Metrics: []Metric{
-					{Metric: "os.cpu.3.idle", Alias: ""},
-					{Metric: "os.cpu.2.idle", Alias: ""},
-					{Metric: "os.cpu.1.idle", Alias: "cpu"},
-				},
-				Hosts:             []string{"staples-lab-1", "staples-lab-2"},
-				Cluster:           []string{"demoapp-1", "demoapp-2"},
-				AddClusterToAlias: false,
-				AddHostToAlias:    false,
-				FunctionList: []Function{
-					{Func: "aggregate.min"},
-				},
-				TimeRange: &tsdb.TimeRange{Now: now, From: "5m", To: "now"},
-			}
-
-			expandeQueries, err := query.Build(availableMetrics)
-			So(err, ShouldBeNil)
-			So(len(expandeQueries), ShouldEqual, 3)
-			So(expandeQueries[0].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.3.idle`|aggregate.min where cluster in ('demoapp-1', 'demoapp-2') and host in ('staples-lab-1', 'staples-lab-2') from %v to %v", from, to))
-			So(expandeQueries[1].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.2.idle`|aggregate.min where cluster in ('demoapp-1', 'demoapp-2') and host in ('staples-lab-1', 'staples-lab-2') from %v to %v", from, to))
-			So(expandeQueries[2].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.1.idle`|aggregate.min {cpu} where cluster in ('demoapp-1', 'demoapp-2') and host in ('staples-lab-1', 'staples-lab-2') from %v to %v", from, to))
-		})
-
-		Convey("With two aggregate functions", func() {
-			query := &Query{
-				Metrics: []Metric{
-					{Metric: "os.cpu.3.idle", Alias: ""},
-				},
-				Hosts:             []string{"staples-lab-1", "staples-lab-2"},
-				Cluster:           []string{"demoapp-1", "demoapp-2"},
-				AddClusterToAlias: false,
-				AddHostToAlias:    false,
-				FunctionList: []Function{
-					{Func: "aggregate.min"},
-					{Func: "aggregate.max"},
-				},
-				TimeRange: &tsdb.TimeRange{Now: now, From: "5m", To: "now"},
-			}
-
-			expandeQueries, err := query.Build(availableMetrics)
-			So(err, ShouldBeNil)
-			So(len(expandeQueries), ShouldEqual, 1)
-			So(expandeQueries[0].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.3.idle`|aggregate.min|aggregate.max where cluster in ('demoapp-1', 'demoapp-2') and host in ('staples-lab-1', 'staples-lab-2') from %v to %v", from, to))
-		})
-
-		Convey("Containing wildcard series", func() {
-			query := &Query{
-				Metrics: []Metric{
-					{Metric: "os.cpu*", Alias: ""},
-				},
-				Hosts:             []string{"staples-lab-1"},
-				AddClusterToAlias: false,
-				AddHostToAlias:    false,
-				TimeRange:         &tsdb.TimeRange{Now: now, From: "5m", To: "now"},
-			}
-
-			expandeQueries, err := query.Build(availableMetrics)
-			So(err, ShouldBeNil)
-			So(len(expandeQueries), ShouldEqual, 4)
-
-			So(expandeQueries[0].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.all.idle` where host in ('staples-lab-1') from %v to %v", from, to))
-			So(expandeQueries[1].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.1.idle` where host in ('staples-lab-1') from %v to %v", from, to))
-			So(expandeQueries[2].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.2.idle` where host in ('staples-lab-1') from %v to %v", from, to))
-			So(expandeQueries[3].RawQuery, ShouldEqual, fmt.Sprintf("`os.cpu.3.idle` where host in ('staples-lab-1') from %v to %v", from, to))
-		})
-	})
-}

+ 20 - 18
pkg/tsdb/mysql/mysql.go

@@ -21,9 +21,8 @@ import (
 )
 )
 
 
 type MysqlExecutor struct {
 type MysqlExecutor struct {
-	datasource *models.DataSource
-	engine     *xorm.Engine
-	log        log.Logger
+	engine *xorm.Engine
+	log    log.Logger
 }
 }
 
 
 type engineCacheType struct {
 type engineCacheType struct {
@@ -38,16 +37,15 @@ var engineCache = engineCacheType{
 }
 }
 
 
 func init() {
 func init() {
-	tsdb.RegisterExecutor("mysql", NewMysqlExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("mysql", NewMysqlExecutor)
 }
 }
 
 
-func NewMysqlExecutor(datasource *models.DataSource) (tsdb.Executor, error) {
+func NewMysqlExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
 	executor := &MysqlExecutor{
 	executor := &MysqlExecutor{
-		datasource: datasource,
-		log:        log.New("tsdb.mysql"),
+		log: log.New("tsdb.mysql"),
 	}
 	}
 
 
-	err := executor.initEngine()
+	err := executor.initEngine(datasource)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -55,18 +53,24 @@ func NewMysqlExecutor(datasource *models.DataSource) (tsdb.Executor, error) {
 	return executor, nil
 	return executor, nil
 }
 }
 
 
-func (e *MysqlExecutor) initEngine() error {
+func (e *MysqlExecutor) initEngine(dsInfo *models.DataSource) error {
 	engineCache.Lock()
 	engineCache.Lock()
 	defer engineCache.Unlock()
 	defer engineCache.Unlock()
 
 
-	if engine, present := engineCache.cache[e.datasource.Id]; present {
-		if version, _ := engineCache.versions[e.datasource.Id]; version == e.datasource.Version {
+	if engine, present := engineCache.cache[dsInfo.Id]; present {
+		if version, _ := engineCache.versions[dsInfo.Id]; version == dsInfo.Version {
 			e.engine = engine
 			e.engine = engine
 			return nil
 			return nil
 		}
 		}
 	}
 	}
 
 
-	cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC", e.datasource.User, e.datasource.Password, "tcp", e.datasource.Url, e.datasource.Database)
+	cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC",
+		dsInfo.User,
+		dsInfo.Password,
+		"tcp",
+		dsInfo.Url,
+		dsInfo.Database)
+
 	e.log.Debug("getEngine", "connection", cnnstr)
 	e.log.Debug("getEngine", "connection", cnnstr)
 
 
 	engine, err := xorm.NewEngine("mysql", cnnstr)
 	engine, err := xorm.NewEngine("mysql", cnnstr)
@@ -76,22 +80,22 @@ func (e *MysqlExecutor) initEngine() error {
 		return err
 		return err
 	}
 	}
 
 
-	engineCache.cache[e.datasource.Id] = engine
+	engineCache.cache[dsInfo.Id] = engine
 	e.engine = engine
 	e.engine = engine
 	return nil
 	return nil
 }
 }
 
 
-func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{
 	result := &tsdb.BatchResult{
 		QueryResults: make(map[string]*tsdb.QueryResult),
 		QueryResults: make(map[string]*tsdb.QueryResult),
 	}
 	}
 
 
-	macroEngine := NewMysqlMacroEngine(context.TimeRange)
+	macroEngine := NewMysqlMacroEngine(tsdbQuery.TimeRange)
 	session := e.engine.NewSession()
 	session := e.engine.NewSession()
 	defer session.Close()
 	defer session.Close()
 	db := session.DB()
 	db := session.DB()
 
 
-	for _, query := range queries {
+	for _, query := range tsdbQuery.Queries {
 		rawSql := query.Model.Get("rawSql").MustString()
 		rawSql := query.Model.Get("rawSql").MustString()
 		if rawSql == "" {
 		if rawSql == "" {
 			continue
 			continue
@@ -272,8 +276,6 @@ func (e MysqlExecutor) TransformToTimeSeries(query *tsdb.Query, rows *core.Rows,
 			rowData.metric = "Unknown"
 			rowData.metric = "Unknown"
 		}
 		}
 
 
-		//e.log.Debug("Rows", "metric", rowData.metric, "time", rowData.time, "value", rowData.value)
-
 		if !rowData.time.Valid {
 		if !rowData.time.Valid {
 			return fmt.Errorf("Found row with no time value")
 			return fmt.Errorf("Found row with no time value")
 		}
 		}

+ 26 - 18
pkg/tsdb/opentsdb/opentsdb.go

@@ -22,20 +22,22 @@ import (
 )
 )
 
 
 type OpenTsdbExecutor struct {
 type OpenTsdbExecutor struct {
-	*models.DataSource
-	httpClient *http.Client
+	//*models.DataSource
+	//httpClient *http.Client
 }
 }
 
 
-func NewOpenTsdbExecutor(datasource *models.DataSource) (tsdb.Executor, error) {
-	httpClient, err := datasource.GetHttpClient()
+func NewOpenTsdbExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
+	/*
+		httpClient, err := datasource.GetHttpClient()
 
 
-	if err != nil {
-		return nil, err
-	}
+		if err != nil {
+			return nil, err
+		}
+	*/
 
 
 	return &OpenTsdbExecutor{
 	return &OpenTsdbExecutor{
-		DataSource: datasource,
-		httpClient: httpClient,
+	//DataSource: datasource,
+	//httpClient: httpClient,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -45,10 +47,10 @@ var (
 
 
 func init() {
 func init() {
 	plog = log.New("tsdb.opentsdb")
 	plog = log.New("tsdb.opentsdb")
-	tsdb.RegisterExecutor("opentsdb", NewOpenTsdbExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("opentsdb", NewOpenTsdbExecutor)
 }
 }
 
 
-func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{}
 	result := &tsdb.BatchResult{}
 
 
 	var tsdbQuery OpenTsdbQuery
 	var tsdbQuery OpenTsdbQuery
@@ -56,7 +58,7 @@ func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
 	tsdbQuery.Start = queryContext.TimeRange.GetFromAsMsEpoch()
 	tsdbQuery.Start = queryContext.TimeRange.GetFromAsMsEpoch()
 	tsdbQuery.End = queryContext.TimeRange.GetToAsMsEpoch()
 	tsdbQuery.End = queryContext.TimeRange.GetToAsMsEpoch()
 
 
-	for _, query := range queries {
+	for _, query := range queryContext.Queries {
 		metric := e.buildMetric(query)
 		metric := e.buildMetric(query)
 		tsdbQuery.Queries = append(tsdbQuery.Queries, metric)
 		tsdbQuery.Queries = append(tsdbQuery.Queries, metric)
 	}
 	}
@@ -65,13 +67,19 @@ func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
 		plog.Debug("OpenTsdb request", "params", tsdbQuery)
 		plog.Debug("OpenTsdb request", "params", tsdbQuery)
 	}
 	}
 
 
-	req, err := e.createRequest(tsdbQuery)
+	req, err := e.createRequest(dsInfo, tsdbQuery)
+	if err != nil {
+		result.Error = err
+		return result
+	}
+
+	httpClient, err := dsInfo.GetHttpClient()
 	if err != nil {
 	if err != nil {
 		result.Error = err
 		result.Error = err
 		return result
 		return result
 	}
 	}
 
 
-	res, err := ctxhttp.Do(ctx, e.httpClient, req)
+	res, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
 	if err != nil {
 		result.Error = err
 		result.Error = err
 		return result
 		return result
@@ -86,8 +94,8 @@ func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
 	return result
 	return result
 }
 }
 
 
-func (e *OpenTsdbExecutor) createRequest(data OpenTsdbQuery) (*http.Request, error) {
-	u, _ := url.Parse(e.Url)
+func (e *OpenTsdbExecutor) createRequest(dsInfo *models.DataSource, data OpenTsdbQuery) (*http.Request, error) {
+	u, _ := url.Parse(dsInfo.Url)
 	u.Path = path.Join(u.Path, "api/query")
 	u.Path = path.Join(u.Path, "api/query")
 
 
 	postData, err := json.Marshal(data)
 	postData, err := json.Marshal(data)
@@ -99,8 +107,8 @@ func (e *OpenTsdbExecutor) createRequest(data OpenTsdbQuery) (*http.Request, err
 	}
 	}
 
 
 	req.Header.Set("Content-Type", "application/json")
 	req.Header.Set("Content-Type", "application/json")
-	if e.BasicAuth {
-		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
+	if dsInfo.BasicAuth {
+		req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.BasicAuthPassword)
 	}
 	}
 
 
 	return req, err
 	return req, err

+ 20 - 16
pkg/tsdb/prometheus/prometheus.go

@@ -7,6 +7,8 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/opentracing/opentracing-go"
+
 	"net/http"
 	"net/http"
 
 
 	"github.com/grafana/grafana/pkg/components/null"
 	"github.com/grafana/grafana/pkg/components/null"
@@ -16,12 +18,9 @@ import (
 	api "github.com/prometheus/client_golang/api"
 	api "github.com/prometheus/client_golang/api"
 	apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
 	apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
 	"github.com/prometheus/common/model"
 	"github.com/prometheus/common/model"
-	//api "github.com/prometheus/client_golang/api"
-	//apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
 )
 )
 
 
 type PrometheusExecutor struct {
 type PrometheusExecutor struct {
-	*models.DataSource
 	Transport *http.Transport
 	Transport *http.Transport
 }
 }
 
 
@@ -37,15 +36,14 @@ func (bat basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, erro
 	return bat.Transport.RoundTrip(req)
 	return bat.Transport.RoundTrip(req)
 }
 }
 
 
-func NewPrometheusExecutor(dsInfo *models.DataSource) (tsdb.Executor, error) {
+func NewPrometheusExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
 	transport, err := dsInfo.GetHttpTransport()
 	transport, err := dsInfo.GetHttpTransport()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	return &PrometheusExecutor{
 	return &PrometheusExecutor{
-		DataSource: dsInfo,
-		Transport:  transport,
+		Transport: transport,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -56,21 +54,21 @@ var (
 
 
 func init() {
 func init() {
 	plog = log.New("tsdb.prometheus")
 	plog = log.New("tsdb.prometheus")
-	tsdb.RegisterExecutor("prometheus", NewPrometheusExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("prometheus", NewPrometheusExecutor)
 	legendFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
 	legendFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
 }
 }
 
 
-func (e *PrometheusExecutor) getClient() (apiv1.API, error) {
+func (e *PrometheusExecutor) getClient(dsInfo *models.DataSource) (apiv1.API, error) {
 	cfg := api.Config{
 	cfg := api.Config{
-		Address:      e.DataSource.Url,
+		Address:      dsInfo.Url,
 		RoundTripper: e.Transport,
 		RoundTripper: e.Transport,
 	}
 	}
 
 
-	if e.BasicAuth {
+	if dsInfo.BasicAuth {
 		cfg.RoundTripper = basicAuthTransport{
 		cfg.RoundTripper = basicAuthTransport{
 			Transport: e.Transport,
 			Transport: e.Transport,
-			username:  e.BasicAuthUser,
-			password:  e.BasicAuthPassword,
+			username:  dsInfo.BasicAuthUser,
+			password:  dsInfo.BasicAuthPassword,
 		}
 		}
 	}
 	}
 
 
@@ -82,15 +80,15 @@ func (e *PrometheusExecutor) getClient() (apiv1.API, error) {
 	return apiv1.NewAPI(client), nil
 	return apiv1.NewAPI(client), nil
 }
 }
 
 
-func (e *PrometheusExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{}
 	result := &tsdb.BatchResult{}
 
 
-	client, err := e.getClient()
+	client, err := e.getClient(dsInfo)
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
 
 
-	query, err := parseQuery(queries, queryContext)
+	query, err := parseQuery(tsdbQuery.Queries, tsdbQuery)
 	if err != nil {
 	if err != nil {
 		return result.WithError(err)
 		return result.WithError(err)
 	}
 	}
@@ -101,6 +99,12 @@ func (e *PrometheusExecutor) Execute(ctx context.Context, queries tsdb.QuerySlic
 		Step:  query.Step,
 		Step:  query.Step,
 	}
 	}
 
 
+	span, ctx := opentracing.StartSpanFromContext(ctx, "alerting.prometheus")
+	span.SetTag("expr", query.Expr)
+	span.SetTag("start_unixnano", int64(query.Start.UnixNano()))
+	span.SetTag("stop_unixnano", int64(query.End.UnixNano()))
+	defer span.Finish()
+
 	value, err := client.QueryRange(ctx, query.Expr, timeRange)
 	value, err := client.QueryRange(ctx, query.Expr, timeRange)
 
 
 	if err != nil {
 	if err != nil {
@@ -134,7 +138,7 @@ func formatLegend(metric model.Metric, query *PrometheusQuery) string {
 	return string(result)
 	return string(result)
 }
 }
 
 
-func parseQuery(queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) (*PrometheusQuery, error) {
+func parseQuery(queries []*tsdb.Query, queryContext *tsdb.TsdbQuery) (*PrometheusQuery, error) {
 	queryModel := queries[0]
 	queryModel := queries[0]
 
 
 	expr, err := queryModel.Model.Get("expr").String()
 	expr, err := queryModel.Model.Get("expr").String()

+ 0 - 21
pkg/tsdb/query_context.go

@@ -1,21 +0,0 @@
-package tsdb
-
-import "sync"
-
-type QueryContext struct {
-	TimeRange   *TimeRange
-	Queries     QuerySlice
-	Results     map[string]*QueryResult
-	ResultsChan chan *BatchResult
-	Lock        sync.RWMutex
-	BatchWaits  sync.WaitGroup
-}
-
-func NewQueryContext(queries QuerySlice, timeRange *TimeRange) *QueryContext {
-	return &QueryContext{
-		TimeRange:   timeRange,
-		Queries:     queries,
-		ResultsChan: make(chan *BatchResult),
-		Results:     make(map[string]*QueryResult),
-	}
-}

+ 36 - 0
pkg/tsdb/query_endpoint.go

@@ -0,0 +1,36 @@
+package tsdb
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/grafana/grafana/pkg/models"
+)
+
+type TsdbQueryEndpoint interface {
+	Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) *BatchResult
+}
+
+var registry map[string]GetTsdbQueryEndpointFn
+
+type GetTsdbQueryEndpointFn func(dsInfo *models.DataSource) (TsdbQueryEndpoint, error)
+
+func init() {
+	registry = make(map[string]GetTsdbQueryEndpointFn)
+}
+
+func getTsdbQueryEndpointFor(dsInfo *models.DataSource) (TsdbQueryEndpoint, error) {
+	if fn, exists := registry[dsInfo.Type]; exists {
+		executor, err := fn(dsInfo)
+		if err != nil {
+			return nil, err
+		}
+
+		return executor, nil
+	}
+	return nil, fmt.Errorf("Could not find executor for data source type: %s", dsInfo.Type)
+}
+
+func RegisterTsdbQueryEndpoint(pluginId string, fn GetTsdbQueryEndpointFn) {
+	registry[pluginId] = fn
+}

+ 12 - 49
pkg/tsdb/request.go

@@ -4,60 +4,23 @@ import (
 	"context"
 	"context"
 )
 )
 
 
-type HandleRequestFunc func(ctx context.Context, req *Request) (*Response, error)
+type HandleRequestFunc func(ctx context.Context, req *TsdbQuery) (*Response, error)
 
 
-func HandleRequest(ctx context.Context, req *Request) (*Response, error) {
-	context := NewQueryContext(req.Queries, req.TimeRange)
-
-	batches, err := getBatches(req)
+func HandleRequest(ctx context.Context, req *TsdbQuery) (*Response, error) {
+	//TODO niceify
+	ds := req.Queries[0].DataSource
+	endpoint, err := getTsdbQueryEndpointFor(ds)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	currentlyExecuting := 0
-
-	for _, batch := range batches {
-		if len(batch.Depends) == 0 {
-			currentlyExecuting += 1
-			batch.Started = true
-			go batch.process(ctx, context)
-		}
-	}
-
-	response := &Response{}
-
-	for currentlyExecuting != 0 {
-		select {
-		case batchResult := <-context.ResultsChan:
-			currentlyExecuting -= 1
-
-			response.BatchTimings = append(response.BatchTimings, batchResult.Timings)
-
-			if batchResult.Error != nil {
-				return nil, batchResult.Error
-			}
-
-			for refId, result := range batchResult.QueryResults {
-				context.Results[refId] = result
-			}
-
-			for _, batch := range batches {
-				// not interested in started batches
-				if batch.Started {
-					continue
-				}
-
-				if batch.allDependenciesAreIn(context) {
-					currentlyExecuting += 1
-					batch.Started = true
-					go batch.process(ctx, context)
-				}
-			}
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		}
+	res := endpoint.Query(ctx, ds, req)
+	if res.Error != nil {
+		return nil, res.Error
 	}
 	}
 
 
-	response.Results = context.Results
-	return response, nil
+	return &Response{
+		Results:      res.QueryResults,
+		BatchTimings: []*BatchTiming{res.Timings},
+	}, nil
 }
 }

+ 7 - 7
pkg/tsdb/testdata/scenarios.go

@@ -11,7 +11,7 @@ import (
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
 )
 )
 
 
-type ScenarioHandler func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult
+type ScenarioHandler func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult
 
 
 type Scenario struct {
 type Scenario struct {
 	Id          string          `json:"id"`
 	Id          string          `json:"id"`
@@ -33,9 +33,9 @@ func init() {
 		Id:   "random_walk",
 		Id:   "random_walk",
 		Name: "Random Walk",
 		Name: "Random Walk",
 
 
-		Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult {
-			timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
-			to := context.TimeRange.GetToAsMsEpoch()
+		Handler: func(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
+			timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
+			to := tsdbQuery.TimeRange.GetToAsMsEpoch()
 
 
 			series := newSeriesForQuery(query)
 			series := newSeriesForQuery(query)
 
 
@@ -60,7 +60,7 @@ func init() {
 	registerScenario(&Scenario{
 	registerScenario(&Scenario{
 		Id:   "no_data_points",
 		Id:   "no_data_points",
 		Name: "No Data Points",
 		Name: "No Data Points",
-		Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult {
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
 			return tsdb.NewQueryResult()
 			return tsdb.NewQueryResult()
 		},
 		},
 	})
 	})
@@ -68,7 +68,7 @@ func init() {
 	registerScenario(&Scenario{
 	registerScenario(&Scenario{
 		Id:   "datapoints_outside_range",
 		Id:   "datapoints_outside_range",
 		Name: "Datapoints Outside Range",
 		Name: "Datapoints Outside Range",
-		Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult {
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
 			queryRes := tsdb.NewQueryResult()
 			queryRes := tsdb.NewQueryResult()
 
 
 			series := newSeriesForQuery(query)
 			series := newSeriesForQuery(query)
@@ -85,7 +85,7 @@ func init() {
 		Id:          "csv_metric_values",
 		Id:          "csv_metric_values",
 		Name:        "CSV Metric Values",
 		Name:        "CSV Metric Values",
 		StringInput: "1,20,90,30,5,0",
 		StringInput: "1,20,90,30,5,0",
-		Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult {
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
 			queryRes := tsdb.NewQueryResult()
 			queryRes := tsdb.NewQueryResult()
 
 
 			stringInput := query.Model.Get("stringInput").MustString()
 			stringInput := query.Model.Get("stringInput").MustString()

+ 5 - 5
pkg/tsdb/testdata/testdata.go

@@ -13,7 +13,7 @@ type TestDataExecutor struct {
 	log log.Logger
 	log log.Logger
 }
 }
 
 
-func NewTestDataExecutor(dsInfo *models.DataSource) (tsdb.Executor, error) {
+func NewTestDataExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
 	return &TestDataExecutor{
 	return &TestDataExecutor{
 		DataSource: dsInfo,
 		DataSource: dsInfo,
 		log:        log.New("tsdb.testdata"),
 		log:        log.New("tsdb.testdata"),
@@ -21,17 +21,17 @@ func NewTestDataExecutor(dsInfo *models.DataSource) (tsdb.Executor, error) {
 }
 }
 
 
 func init() {
 func init() {
-	tsdb.RegisterExecutor("grafana-testdata-datasource", NewTestDataExecutor)
+	tsdb.RegisterTsdbQueryEndpoint("grafana-testdata-datasource", NewTestDataExecutor)
 }
 }
 
 
-func (e *TestDataExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
+func (e *TestDataExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
 	result := &tsdb.BatchResult{}
 	result := &tsdb.BatchResult{}
 	result.QueryResults = make(map[string]*tsdb.QueryResult)
 	result.QueryResults = make(map[string]*tsdb.QueryResult)
 
 
-	for _, query := range queries {
+	for _, query := range tsdbQuery.Queries {
 		scenarioId := query.Model.Get("scenarioId").MustString("random_walk")
 		scenarioId := query.Model.Get("scenarioId").MustString("random_walk")
 		if scenario, exist := ScenarioRegistry[scenarioId]; exist {
 		if scenario, exist := ScenarioRegistry[scenarioId]; exist {
-			result.QueryResults[query.RefId] = scenario.Handler(query, context)
+			result.QueryResults[query.RefId] = scenario.Handler(query, tsdbQuery)
 			result.QueryResults[query.RefId].RefId = query.RefId
 			result.QueryResults[query.RefId].RefId = query.RefId
 		} else {
 		} else {
 			e.log.Error("Scenario not found", "scenarioId", scenarioId)
 			e.log.Error("Scenario not found", "scenarioId", scenarioId)

+ 7 - 108
pkg/tsdb/tsdb_test.go

@@ -3,60 +3,15 @@ package tsdb
 import (
 import (
 	"context"
 	"context"
 	"testing"
 	"testing"
-	"time"
 
 
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
 )
 )
 
 
 func TestMetricQuery(t *testing.T) {
 func TestMetricQuery(t *testing.T) {
-
-	Convey("When batches groups for query", t, func() {
-
-		Convey("Given 3 queries for 2 data sources", func() {
-			request := &Request{
-				Queries: QuerySlice{
-					{RefId: "A", DataSource: &models.DataSource{Id: 1}},
-					{RefId: "B", DataSource: &models.DataSource{Id: 1}},
-					{RefId: "C", DataSource: &models.DataSource{Id: 2}},
-				},
-			}
-
-			batches, err := getBatches(request)
-			So(err, ShouldBeNil)
-
-			Convey("Should group into two batches", func() {
-				So(len(batches), ShouldEqual, 2)
-			})
-		})
-
-		Convey("Given query 2 depends on query 1", func() {
-			request := &Request{
-				Queries: QuerySlice{
-					{RefId: "A", DataSource: &models.DataSource{Id: 1}},
-					{RefId: "B", DataSource: &models.DataSource{Id: 2}},
-					{RefId: "C", DataSource: &models.DataSource{Id: 3}, Depends: []string{"A", "B"}},
-				},
-			}
-
-			batches, err := getBatches(request)
-			So(err, ShouldBeNil)
-
-			Convey("Should return three batch groups", func() {
-				So(len(batches), ShouldEqual, 3)
-			})
-
-			Convey("Group 3 should have group 1 and 2 as dependencies", func() {
-				So(batches[2].Depends["A"], ShouldEqual, true)
-				So(batches[2].Depends["B"], ShouldEqual, true)
-			})
-
-		})
-	})
-
 	Convey("When executing request with one query", t, func() {
 	Convey("When executing request with one query", t, func() {
-		req := &Request{
-			Queries: QuerySlice{
+		req := &TsdbQuery{
+			Queries: []*Query{
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 			},
 			},
 		}
 		}
@@ -74,8 +29,8 @@ func TestMetricQuery(t *testing.T) {
 	})
 	})
 
 
 	Convey("When executing one request with two queries from same data source", t, func() {
 	Convey("When executing one request with two queries from same data source", t, func() {
-		req := &Request{
-			Queries: QuerySlice{
+		req := &TsdbQuery{
+			Queries: []*Query{
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 				{RefId: "B", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 				{RefId: "B", DataSource: &models.DataSource{Id: 1, Type: "test"}},
 			},
 			},
@@ -99,26 +54,9 @@ func TestMetricQuery(t *testing.T) {
 
 
 	})
 	})
 
 
-	Convey("When executing one request with three queries from different datasources", t, func() {
-		req := &Request{
-			Queries: QuerySlice{
-				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}},
-				{RefId: "B", DataSource: &models.DataSource{Id: 1, Type: "test"}},
-				{RefId: "C", DataSource: &models.DataSource{Id: 2, Type: "test"}},
-			},
-		}
-
-		res, err := HandleRequest(context.TODO(), req)
-		So(err, ShouldBeNil)
-
-		Convey("Should have been batched in two requests", func() {
-			So(len(res.BatchTimings), ShouldEqual, 2)
-		})
-	})
-
 	Convey("When query uses data source of unknown type", t, func() {
 	Convey("When query uses data source of unknown type", t, func() {
-		req := &Request{
-			Queries: QuerySlice{
+		req := &TsdbQuery{
+			Queries: []*Query{
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "asdasdas"}},
 				{RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "asdasdas"}},
 			},
 			},
 		}
 		}
@@ -126,50 +64,11 @@ func TestMetricQuery(t *testing.T) {
 		_, err := HandleRequest(context.TODO(), req)
 		_, err := HandleRequest(context.TODO(), req)
 		So(err, ShouldNotBeNil)
 		So(err, ShouldNotBeNil)
 	})
 	})
-
-	Convey("When executing request that depend on other query", t, func() {
-		req := &Request{
-			Queries: QuerySlice{
-				{
-					RefId: "A", DataSource: &models.DataSource{Id: 1, Type: "test"},
-				},
-				{
-					RefId: "B", DataSource: &models.DataSource{Id: 2, Type: "test"}, Depends: []string{"A"},
-				},
-			},
-		}
-
-		fakeExecutor := registerFakeExecutor()
-		fakeExecutor.HandleQuery("A", func(c *QueryContext) *QueryResult {
-			time.Sleep(10 * time.Millisecond)
-			return &QueryResult{
-				Series: TimeSeriesSlice{
-					&TimeSeries{Name: "Ares"},
-				}}
-		})
-		fakeExecutor.HandleQuery("B", func(c *QueryContext) *QueryResult {
-			return &QueryResult{
-				Series: TimeSeriesSlice{
-					&TimeSeries{Name: "Bres+" + c.Results["A"].Series[0].Name},
-				}}
-		})
-
-		res, err := HandleRequest(context.TODO(), req)
-		So(err, ShouldBeNil)
-
-		Convey("Should have been batched in two requests", func() {
-			So(len(res.BatchTimings), ShouldEqual, 2)
-		})
-
-		Convey("Query B should have access to Query A results", func() {
-			So(res.Results["B"].Series[0].Name, ShouldEqual, "Bres+Ares")
-		})
-	})
 }
 }
 
 
 func registerFakeExecutor() *FakeExecutor {
 func registerFakeExecutor() *FakeExecutor {
 	executor, _ := NewFakeExecutor(nil)
 	executor, _ := NewFakeExecutor(nil)
-	RegisterExecutor("test", func(dsInfo *models.DataSource) (Executor, error) {
+	RegisterTsdbQueryEndpoint("test", func(dsInfo *models.DataSource) (TsdbQueryEndpoint, error) {
 		return executor, nil
 		return executor, nil
 	})
 	})
 
 

+ 2 - 0
public/app/app.ts

@@ -9,6 +9,7 @@ import 'angular-sanitize';
 import 'angular-dragdrop';
 import 'angular-dragdrop';
 import 'angular-bindonce';
 import 'angular-bindonce';
 import 'angular-ui';
 import 'angular-ui';
+import 'ngreact';
 
 
 import $ from 'jquery';
 import $ from 'jquery';
 import angular from 'angular';
 import angular from 'angular';
@@ -84,6 +85,7 @@ export class GrafanaApp {
       'pasvaz.bindonce',
       'pasvaz.bindonce',
       'ui.bootstrap',
       'ui.bootstrap',
       'ui.bootstrap.tpls',
       'ui.bootstrap.tpls',
+      'react'
     ];
     ];
 
 
     var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
     var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];

+ 39 - 0
public/app/core/components/PasswordStrength.tsx

@@ -0,0 +1,39 @@
+import * as React from 'react';
+import 'react-dom';
+import coreModule from '../core_module';
+
+export interface IProps {
+  password: string;
+}
+
+export class PasswordStrength extends React.Component<IProps, any> {
+
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    let strengthText = "strength: strong like a bull.";
+    let strengthClass = "password-strength-good";
+
+    if (this.props.password.length < 4) {
+      strengthText = "strength: weak sauce.";
+      strengthClass = "password-strength-bad";
+    }
+
+    if (this.props.password.length <= 8) {
+      strengthText = "strength: you can do better.";
+      strengthClass = "password-strength-ok";
+    }
+
+    return (
+      <div className={`password-strength small ${strengthClass}`}>
+        <em>{strengthText}</em>
+      </div>
+    );
+  }
+}
+
+coreModule.directive('passwordStrength', function(reactDirective) {
+  return reactDirective(PasswordStrength, ['password']);
+});

+ 0 - 58
public/app/core/components/collapse_box.ts

@@ -1,58 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import coreModule from 'app/core/core_module';
-
-const template = `
-<div class="collapse-box">
-  <div class="collapse-box__header">
-    <a class="collapse-box__header-title pointer" ng-click="ctrl.toggle()">
-      <span class="fa fa-fw fa-caret-right" ng-hide="ctrl.isOpen"></span>
-      <span class="fa fa-fw fa-caret-down" ng-hide="!ctrl.isOpen"></span>
-      {{ctrl.title}}
-    </a>
-    <div class="collapse-box__header-actions" ng-transclude="actions" ng-if="ctrl.isOpen"></div>
-  </div>
-  <div class="collapse-box__body" ng-transclude="body" ng-if="ctrl.isOpen">
-  </div>
-</div>
-`;
-
-export class CollapseBoxCtrl {
-  isOpen: boolean;
-  stateChanged: () => void;
-
-  /** @ngInject **/
-  constructor(private $timeout) {
-    this.isOpen = false;
-  }
-
-  toggle() {
-    this.isOpen = !this.isOpen;
-    this.$timeout(() => {
-      this.stateChanged();
-    });
-  }
-}
-
-export function collapseBox() {
-  return {
-    restrict: 'E',
-    template: template,
-    controller: CollapseBoxCtrl,
-    bindToController: true,
-    controllerAs: 'ctrl',
-    scope: {
-      "title": "@",
-      "isOpen": "=?",
-      "stateChanged": "&"
-    },
-    transclude: {
-      'actions': '?collapseBoxActions',
-      'body': 'collapseBoxBody',
-    },
-    link: function(scope, elem, attrs) {
-    }
-  };
-}
-
-coreModule.directive('collapseBox', collapseBox);

+ 1 - 4
public/app/core/components/colorpicker.ts

@@ -1,8 +1,5 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 var template = `
 var template = `
@@ -37,7 +34,7 @@ export class ColorPickerCtrl {
   showAxisControls: boolean;
   showAxisControls: boolean;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, private $rootScope) {
+  constructor(private $scope, $rootScope) {
     this.colors = $rootScope.colors;
     this.colors = $rootScope.colors;
     this.autoClose = $scope.autoClose;
     this.autoClose = $scope.autoClose;
     this.series = $scope.series;
     this.series = $scope.series;

+ 0 - 3
public/app/core/components/dashboard_selector.ts

@@ -1,8 +1,5 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 var template = `
 var template = `

+ 0 - 1
public/app/core/components/form_dropdown/form_dropdown.ts

@@ -1,6 +1,5 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
 import _ from 'lodash';
 import _ from 'lodash';
 import $ from 'jquery';
 import $ from 'jquery';
 import coreModule from '../../core_module';
 import coreModule from '../../core_module';

+ 0 - 2
public/app/core/components/grafana_app.ts

@@ -1,9 +1,7 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
 import config from 'app/core/config';
 import config from 'app/core/config';
-import store from 'app/core/store';
 import _ from 'lodash';
 import _ from 'lodash';
-import angular from 'angular';
 import $ from 'jquery';
 import $ from 'jquery';
 
 
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';

+ 1 - 1
public/app/core/components/help/help.ts

@@ -8,7 +8,7 @@ export class HelpCtrl {
   shortcuts: any;
   shortcuts: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, $sce) {
+  constructor() {
     this.tabIndex = 0;
     this.tabIndex = 0;
     this.shortcuts = {
     this.shortcuts = {
       'Global': [
       'Global': [

+ 0 - 1
public/app/core/components/info_popover.ts

@@ -1,7 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
 import _ from 'lodash';
 import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import Drop from 'tether-drop';
 import Drop from 'tether-drop';
 
 

+ 1 - 1
public/app/core/components/json_explorer/helpers.ts

@@ -60,7 +60,7 @@ export function getValuePreview (object: Object, value: string): string {
   if (type === 'string') {
   if (type === 'string') {
     value = '"' + escapeString(value) + '"';
     value = '"' + escapeString(value) + '"';
   }
   }
-  if (type === 'function'){
+  if (type === 'function') {
 
 
     // Remove content of the function
     // Remove content of the function
     return object.toString()
     return object.toString()

+ 1 - 2
public/app/core/components/json_explorer/json_explorer.ts

@@ -6,7 +6,6 @@ import {
   getObjectName,
   getObjectName,
   getType,
   getType,
   getValuePreview,
   getValuePreview,
-  getPreview,
   cssClass,
   cssClass,
   createElement
   createElement
 } from './helpers';
 } from './helpers';
@@ -191,7 +190,7 @@ export class JsonExplorer {
     if (this.element) {
     if (this.element) {
       if (this.isOpen) {
       if (this.isOpen) {
         this.appendChildren(this.config.animateOpen);
         this.appendChildren(this.config.animateOpen);
-      } else{
+      } else {
         this.removeChildren(this.config.animateClose);
         this.removeChildren(this.config.animateClose);
       }
       }
       this.element.classList.toggle(cssClass('open'));
       this.element.classList.toggle(cssClass('open'));

+ 0 - 3
public/app/core/components/layout_selector/layout_selector.ts

@@ -1,9 +1,6 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
 import store from 'app/core/store';
 import store from 'app/core/store';
-import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 var template = `
 var template = `

+ 2 - 5
public/app/core/components/navbar/navbar.ts

@@ -1,16 +1,13 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from '../../core_module';
 import coreModule from '../../core_module';
-import {NavModel, NavModelItem}  from '../../nav_model_srv';
+import {NavModel}  from '../../nav_model_srv';
 
 
 export class NavbarCtrl {
 export class NavbarCtrl {
   model: NavModel;
   model: NavModel;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, private $rootScope, private contextSrv) {
+  constructor(private $rootScope) {
   }
   }
 
 
   showSearch() {
   showSearch() {

+ 0 - 1
public/app/core/components/org_switcher.ts

@@ -1,7 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
-import config from 'app/core/config';
 import {contextSrv} from 'app/core/services/context_srv';
 import {contextSrv} from 'app/core/services/context_srv';
 
 
 const template = `
 const template = `

+ 1 - 1
public/app/core/components/row_ctrl.ts

@@ -30,7 +30,7 @@ export class DashboardRowCtrl {
   dashboard: any;
   dashboard: any;
   panel: any;
   panel: any;
 
 
-  constructor(private $rootScope) {
+  constructor() {
     this.panel.hiddenPanels = this.panel.hiddenPanels || [];
     this.panel.hiddenPanels = this.panel.hiddenPanels || [];
   }
   }
 
 

+ 0 - 1
public/app/core/components/scroll/scroll.ts

@@ -2,7 +2,6 @@
 
 
 import GeminiScrollbar from 'gemini-scrollbar';
 import GeminiScrollbar from 'gemini-scrollbar';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
-import _ from 'lodash';
 
 
 export function geminiScrollbar() {
 export function geminiScrollbar() {
   return {
   return {

+ 1 - 5
public/app/core/components/search/search.ts

@@ -1,11 +1,7 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import angular from 'angular';
-import config from 'app/core/config';
 import _ from 'lodash';
 import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from '../../core_module';
 import coreModule from '../../core_module';
-import appEvents from 'app/core/app_events';
 
 
 export class SearchCtrl {
 export class SearchCtrl {
   isOpen: boolean;
   isOpen: boolean;
@@ -22,7 +18,7 @@ export class SearchCtrl {
   openCompleted: boolean;
   openCompleted: boolean;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, private $location, private $timeout, private backendSrv, private contextSrv, private $rootScope) {
+  constructor($scope, private $location, private $timeout, private backendSrv, public contextSrv, $rootScope) {
     $rootScope.onAppEvent('show-dash-search', this.openSearch.bind(this), $scope);
     $rootScope.onAppEvent('show-dash-search', this.openSearch.bind(this), $scope);
     $rootScope.onAppEvent('hide-dash-search', this.closeSearch.bind(this), $scope);
     $rootScope.onAppEvent('hide-dash-search', this.closeSearch.bind(this), $scope);
   }
   }

+ 1 - 1
public/app/core/components/sidemenu/sidemenu.ts

@@ -1,7 +1,7 @@
 ///<reference path="../../../headers/common.d.ts" />
 ///<reference path="../../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
 import _ from 'lodash';
 import _ from 'lodash';
+import config from 'app/core/config';
 import $ from 'jquery';
 import $ from 'jquery';
 import coreModule from '../../core_module';
 import coreModule from '../../core_module';
 
 

+ 0 - 4
public/app/core/components/switch.ts

@@ -1,10 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
-import Drop from 'tether-drop';
 
 
 var template = `
 var template = `
 <label for="check-{{ctrl.id}}" class="gf-form-label {{ctrl.labelClass}} pointer" ng-show="ctrl.label">
 <label for="check-{{ctrl.id}}" class="gf-form-label {{ctrl.labelClass}} pointer" ng-show="ctrl.label">

+ 1 - 2
public/app/core/components/user_group_picker.ts

@@ -1,5 +1,4 @@
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
 import _ from 'lodash';
 import _ from 'lodash';
 
 
 const template = `
 const template = `
@@ -17,7 +16,7 @@ export class UserGroupPickerCtrl {
   debouncedSearchGroups: any;
   debouncedSearchGroups: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private backendSrv, private $scope, $sce, private uiSegmentSrv) {
+  constructor(private backendSrv) {
     this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {'leading': true, 'trailing': false});
     this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {'leading': true, 'trailing': false});
     this.reset();
     this.reset();
   }
   }

+ 1 - 2
public/app/core/components/user_picker.ts

@@ -1,5 +1,4 @@
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
 import _ from 'lodash';
 import _ from 'lodash';
 
 
 const template = `
 const template = `
@@ -17,7 +16,7 @@ export class UserPickerCtrl {
   userPicked: any;
   userPicked: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private backendSrv, private $scope, $sce) {
+  constructor(private backendSrv) {
     this.reset();
     this.reset();
     this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {'leading': true, 'trailing': false});
     this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {'leading': true, 'trailing': false});
   }
   }

+ 0 - 32
public/app/core/components/wizard/wizard.html

@@ -1,32 +0,0 @@
-<div class="modal-body">
-	<div class="modal-header">
-		<h2 class="modal-header-title">
-			<i class="fa fa-cog fa-spin"></i>
-			<span class="p-l-1">{{model.name}}</span>
-		</h2>
-
-		<a class="modal-header-close" ng-click="dismiss();">
-			<i class="fa fa-remove"></i>
-		</a>
-	</div>
-
-	<div class="modal-content">
-    <div ng-if="activeStep">
-
-    </div>
-
-		<!-- <table class="filter&#45;table"> -->
-		<!-- 	<tbody> -->
-		<!-- 		<tr ng&#45;repeat="step in model.steps"> -->
-		<!-- 			<td>{{step.name}}</td> -->
-		<!-- 			<td>{{step.status}}</td> -->
-		<!-- 			<td width="1%"> -->
-		<!-- 				<i class="fa fa&#45;check" style="color: #39A039"></i> -->
-		<!-- 			</td> -->
-		<!-- 		</tr> -->
-		<!-- 	</tbody> -->
-		<!-- </table> -->
-	</div>
-
-</div>
-

+ 0 - 73
public/app/core/components/wizard/wizard.ts

@@ -1,73 +0,0 @@
-///<reference path="../../../headers/common.d.ts" />
-
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
-
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
-
-export class WizardSrv {
-  /** @ngInject */
-  constructor() {
-  }
-}
-
-export interface WizardStep {
-  name: string;
-  type: string;
-  process: any;
-}
-
-export class SelectOptionStep {
-  type: string;
-  name: string;
-  fulfill: any;
-
-  constructor() {
-    this.type = 'select';
-  }
-
-  process() {
-    return new Promise((fulfill, reject) => {
-
-    });
-  }
-}
-
-export class WizardFlow {
-  name: string;
-  steps: WizardStep[];
-
-  constructor(name) {
-    this.name = name;
-    this.steps = [];
-  }
-
-  addStep(step) {
-    this.steps.push(step);
-  }
-
-  next(index) {
-    var step = this.steps[0];
-
-    return step.process().then(() => {
-      if (this.steps.length === index+1) {
-        return;
-      }
-
-      return this.next(index+1);
-    });
-  }
-
-  start() {
-    appEvents.emit('show-modal', {
-      src: 'public/app/core/components/wizard/wizard.html',
-      model: this
-    });
-
-    return this.next(0);
-  }
-}
-
-coreModule.service('wizardSrv', WizardSrv);

+ 3 - 3
public/app/core/controllers/signup_ctrl.ts

@@ -8,9 +8,9 @@ export class SignUpCtrl {
   /** @ngInject */
   /** @ngInject */
   constructor(
   constructor(
       private $scope: any,
       private $scope: any,
-      private $location: any,
-      private contextSrv: any,
-      private backendSrv: any) {
+      private backendSrv: any,
+      $location: any,
+      contextSrv: any) {
 
 
     contextSrv.sidemenu = false;
     contextSrv.sidemenu = false;
     $scope.ctrl = this;
     $scope.ctrl = this;

+ 2 - 4
public/app/core/core.ts

@@ -35,7 +35,6 @@ import {layoutSelector} from './components/layout_selector/layout_selector';
 import {switchDirective} from './components/switch';
 import {switchDirective} from './components/switch';
 import {dashboardSelector} from './components/dashboard_selector';
 import {dashboardSelector} from './components/dashboard_selector';
 import {queryPartEditorDirective} from './components/query_part/query_part_editor';
 import {queryPartEditorDirective} from './components/query_part/query_part_editor';
-import {WizardFlow} from './components/wizard/wizard';
 import {formDropdownDirective} from './components/form_dropdown/form_dropdown';
 import {formDropdownDirective} from './components/form_dropdown/form_dropdown';
 import 'app/core/controllers/all';
 import 'app/core/controllers/all';
 import 'app/core/services/all';
 import 'app/core/services/all';
@@ -48,7 +47,7 @@ import {assignModelProperties} from './utils/model_utils';
 import {contextSrv} from './services/context_srv';
 import {contextSrv} from './services/context_srv';
 import {KeybindingSrv} from './services/keybindingSrv';
 import {KeybindingSrv} from './services/keybindingSrv';
 import {helpModal} from './components/help/help';
 import {helpModal} from './components/help/help';
-import {collapseBox} from './components/collapse_box';
+import {PasswordStrength} from './components/PasswordStrength';
 import {JsonExplorer} from './components/json_explorer/json_explorer';
 import {JsonExplorer} from './components/json_explorer/json_explorer';
 import {NavModelSrv, NavModel} from './nav_model_srv';
 import {NavModelSrv, NavModel} from './nav_model_srv';
 import {userPicker} from './components/user_picker';
 import {userPicker} from './components/user_picker';
@@ -73,14 +72,12 @@ export {
   appEvents,
   appEvents,
   dashboardSelector,
   dashboardSelector,
   queryPartEditorDirective,
   queryPartEditorDirective,
-  WizardFlow,
   colors,
   colors,
   formDropdownDirective,
   formDropdownDirective,
   assignModelProperties,
   assignModelProperties,
   contextSrv,
   contextSrv,
   KeybindingSrv,
   KeybindingSrv,
   helpModal,
   helpModal,
-  collapseBox,
   JsonExplorer,
   JsonExplorer,
   NavModelSrv,
   NavModelSrv,
   NavModel,
   NavModel,
@@ -89,4 +86,5 @@ export {
   geminiScrollbar,
   geminiScrollbar,
   gfPageDirective,
   gfPageDirective,
   orgSwitcher,
   orgSwitcher,
+  PasswordStrength,
 };
 };

+ 1 - 1
public/app/core/directives/diff-view.ts

@@ -7,7 +7,7 @@ export class DeltaCtrl {
   observer: any;
   observer: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $rootScope) {
+  constructor($rootScope) {
     const waitForCompile = function(mutations) {
     const waitForCompile = function(mutations) {
       if (mutations.length === 1) {
       if (mutations.length === 1) {
         this.$rootScope.appEvent('json-diff-ready');
         this.$rootScope.appEvent('json-diff-ready');

+ 1 - 1
public/app/core/directives/password_strength.js

@@ -4,7 +4,7 @@ define([
 function (coreModule) {
 function (coreModule) {
   'use strict';
   'use strict';
 
 
-  coreModule.default.directive('passwordStrength', function() {
+  coreModule.default.directive('passwordStrength2', function() {
     var template = '<div class="password-strength small" ng-if="!loginMode" ng-class="strengthClass">' +
     var template = '<div class="password-strength small" ng-if="!loginMode" ng-class="strengthClass">' +
       '<em>{{strengthText}}</em>' +
       '<em>{{strengthText}}</em>' +
       '</div>';
       '</div>';

+ 0 - 2
public/app/core/directives/rebuild_on_change.ts

@@ -1,7 +1,5 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import angular from 'angular';
-import _ from 'lodash';
 import $ from 'jquery';
 import $ from 'jquery';
 
 
 import coreModule from '../core_module';
 import coreModule from '../core_module';

+ 0 - 1
public/app/core/live/live_srv.ts

@@ -2,7 +2,6 @@
 
 
 import _ from 'lodash';
 import _ from 'lodash';
 import config from 'app/core/config';
 import config from 'app/core/config';
-import coreModule from 'app/core/core_module';
 
 
 import {Observable} from 'vendor/npm/rxjs/Observable';
 import {Observable} from 'vendor/npm/rxjs/Observable';
 
 

+ 0 - 1
public/app/core/profiler.ts

@@ -1,7 +1,6 @@
 ///<reference path="../headers/common.d.ts" />
 ///<reference path="../headers/common.d.ts" />
 
 
 import $ from 'jquery';
 import $ from 'jquery';
-import _ from 'lodash';
 import angular from 'angular';
 import angular from 'angular';
 
 
 export class Profiler {
 export class Profiler {

+ 0 - 1
public/app/core/routes/routes.ts

@@ -2,7 +2,6 @@
 
 
 import './dashboard_loaders';
 import './dashboard_loaders';
 
 
-import angular from 'angular';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import {BundleLoader} from './bundle_loader';
 import {BundleLoader} from './bundle_loader';
 
 

+ 1 - 2
public/app/core/services/alert_srv.ts

@@ -2,7 +2,6 @@
 
 
 import angular from 'angular';
 import angular from 'angular';
 import _ from 'lodash';
 import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import appEvents from 'app/core/app_events';
 import appEvents from 'app/core/app_events';
 
 
@@ -10,7 +9,7 @@ export class AlertSrv {
   list: any[];
   list: any[];
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $timeout, private $sce, private $rootScope, private $modal) {
+  constructor(private $timeout, private $rootScope, private $modal) {
     this.list = [];
     this.list = [];
   }
   }
 
 

+ 1 - 3
public/app/core/services/backend_srv.ts

@@ -1,8 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import angular from 'angular';
 import _ from 'lodash';
 import _ from 'lodash';
-import config from 'app/core/config';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import appEvents from 'app/core/app_events';
 import appEvents from 'app/core/app_events';
 
 
@@ -12,7 +10,7 @@ export class BackendSrv {
   private noBackendCache: boolean;
   private noBackendCache: boolean;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $http, private alertSrv, private $rootScope, private $q, private $timeout, private contextSrv) {
+  constructor(private $http, private alertSrv, private $q, private $timeout, private contextSrv) {
   }
   }
 
 
   get(url, params?) {
   get(url, params?) {

+ 1 - 1
public/app/core/services/dynamic_directive_srv.ts

@@ -6,7 +6,7 @@ import coreModule from '../core_module';
 class DynamicDirectiveSrv {
 class DynamicDirectiveSrv {
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $compile, private $parse, private $rootScope) {}
+  constructor(private $compile, private $rootScope) {}
 
 
   addDirective(element, name, scope) {
   addDirective(element, name, scope) {
     var child = angular.element(document.createElement(name));
     var child = angular.element(document.createElement(name));

+ 1 - 4
public/app/core/services/keybindingSrv.ts

@@ -14,10 +14,7 @@ export class KeybindingSrv {
   /** @ngInject */
   /** @ngInject */
   constructor(
   constructor(
     private $rootScope,
     private $rootScope,
-    private $modal,
-    private $location,
-    private contextSrv,
-    private $timeout) {
+    private $location) {
 
 
     // clear out all shortcuts on route change
     // clear out all shortcuts on route change
     $rootScope.$on('$routeChangeSuccess', () => {
     $rootScope.$on('$routeChangeSuccess', () => {

+ 0 - 2
public/app/core/services/popover_srv.ts

@@ -1,8 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
 import _ from 'lodash';
 import _ from 'lodash';
-import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import Drop from 'tether-drop';
 import Drop from 'tether-drop';
 
 

+ 0 - 4
public/app/core/services/util_srv.ts

@@ -1,9 +1,5 @@
 ///<reference path="../../headers/common.d.ts" />
 ///<reference path="../../headers/common.d.ts" />
 
 
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
-
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import appEvents from 'app/core/app_events';
 import appEvents from 'app/core/app_events';
 
 

+ 14 - 0
public/app/core/specs/PasswordStrength_specs.tsx

@@ -0,0 +1,14 @@
+// import React from 'react';
+// import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
+// import {shallow} from 'enzyme';
+//
+// import {PasswordStrength} from '../components/PasswordStrength';
+//
+// describe('PasswordStrength', () => {
+//
+//   it.skip('should have class bad if length below 4', () => {
+//     const wrapper = shallow(<PasswordStrength password="asd" />);
+//     expect(wrapper.find(".password-strength-bad")).to.have.length(3);
+//   });
+// });
+//

+ 4 - 3
public/app/core/time_series2.ts

@@ -168,15 +168,16 @@ export default class TimeSeries {
         if (currentValue < this.stats.min) {
         if (currentValue < this.stats.min) {
           this.stats.min = currentValue;
           this.stats.min = currentValue;
         }
         }
-        if (this.stats.first === null){
+
+        if (this.stats.first === null) {
           this.stats.first = currentValue;
           this.stats.first = currentValue;
-        }else{
+        } else {
           if (previousValue > currentValue) {   // counter reset
           if (previousValue > currentValue) {   // counter reset
             previousDeltaUp = false;
             previousDeltaUp = false;
             if (i === this.datapoints.length-1) {  // reset on last
             if (i === this.datapoints.length-1) {  // reset on last
                 this.stats.delta += currentValue;
                 this.stats.delta += currentValue;
             }
             }
-          }else{
+          } else {
             if (previousDeltaUp) {
             if (previousDeltaUp) {
               this.stats.delta += currentValue - previousValue;    // normal increment
               this.stats.delta += currentValue - previousValue;    // normal increment
             } else {
             } else {

+ 0 - 6
public/app/core/utils/emitter.ts

@@ -2,12 +2,6 @@
 
 
 import EventEmitter from 'eventemitter3';
 import EventEmitter from 'eventemitter3';
 
 
-var hasOwnProp = {}.hasOwnProperty;
-
-function createName(name) {
-    return '$' + name;
-}
-
 export class Emitter {
 export class Emitter {
   emitter: any;
   emitter: any;
 
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác