From 6e66b8a0fa2bb861affd00f433f5de4062e8352f Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Mon, 28 Sep 2015 13:32:53 +0100 Subject: [PATCH 01/13] Add prometheus datasource --- .../app/plugins/datasource/prometheus/datasource.js | 12 +++++++----- .../app/plugins/datasource/prometheus/queryCtrl.js | 2 +- public/test/specs/prometheus-datasource-specs.js | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index e6eaa71a392..158d2cf6ba5 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -1,17 +1,18 @@ define([ 'angular', 'lodash', - 'kbn', 'moment', 'app/core/utils/datemath', './directives', './queryCtrl', ], -function (angular, _, kbn, dateMath) { +function (angular, _, moment, dateMath) { 'use strict'; var module = angular.module('grafana.services'); + var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/; + module.factory('PrometheusDatasource', function($q, backendSrv, templateSrv) { function PrometheusDatasource(datasource) { @@ -183,13 +184,14 @@ function (angular, _, kbn, dateMath) { }; PrometheusDatasource.prototype.calculateInterval = function(interval, intervalFactor) { - var sec = kbn.interval_to_seconds(interval); - + var m = interval.match(durationSplitRegexp); + var dur = moment.duration(parseInt(m[1]), m[2]); + var sec = dur.asSeconds(); if (sec < 1) { sec = 1; } - return sec * intervalFactor; + return Math.floor(sec * intervalFactor) + 's'; }; function transformMetricData(md, options) { diff --git a/public/app/plugins/datasource/prometheus/queryCtrl.js b/public/app/plugins/datasource/prometheus/queryCtrl.js index 88257c824f5..08f8e899c68 100644 --- a/public/app/plugins/datasource/prometheus/queryCtrl.js +++ b/public/app/plugins/datasource/prometheus/queryCtrl.js @@ -117,7 +117,7 @@ function (angular, _, kbn, dateMath) { $scope.calculateInterval = function() { var interval = $scope.target.interval || $scope.interval; var calculatedInterval = $scope.datasource.calculateInterval(interval, $scope.target.intervalFactor); - $scope.target.calculatedInterval = kbn.secondsToHms(calculatedInterval); + $scope.target.calculatedInterval = calculatedInterval; }; // TODO: validate target diff --git a/public/test/specs/prometheus-datasource-specs.js b/public/test/specs/prometheus-datasource-specs.js index c331b82385c..c65b8771851 100644 --- a/public/test/specs/prometheus-datasource-specs.js +++ b/public/test/specs/prometheus-datasource-specs.js @@ -21,7 +21,7 @@ define([ var results; var urlExpected = '/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + - '&start=1443438675&end=1443460275&step=60'; + '&start=1443438675&end=1443460275&step=60s'; var query = { range: { from: moment(1443438674760), to: moment(1443460274760) }, targets: [{ expr: 'test{job="testjob"}' }], From cf0748895ed578965ccbc003ba5f6474269d3324 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Tue, 29 Sep 2015 15:54:47 +0100 Subject: [PATCH 02/13] Prometheus template params fixes --- .../plugins/datasource/prometheus/datasource.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index 158d2cf6ba5..dd8ba8e2b3b 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -130,8 +130,16 @@ function (angular, _, moment, dateMath) { PrometheusDatasource.prototype.metricFindQuery = function(query) { var url; - var metricsQuery = query.match(/^[a-zA-Z_:*][a-zA-Z0-9_:*]*/); - var labelValuesQuery = query.match(/^label_values\((.+)\)/); + var interpolated; + try { + interpolated = templateSrv.replace(query); + } + catch (err) { + return $q.reject(err); + } + + var metricsQuery = interpolated.match(/^[a-zA-Z_:*][a-zA-Z0-9_:*]*/); + var labelValuesQuery = interpolated.match(/^label_values\((.+)\)/); if (labelValuesQuery) { // return label values @@ -163,11 +171,12 @@ function (angular, _, moment, dateMath) { }); } else { // if query contains full metric name, return metric name and label list - url = '/api/v1/query?query=' + encodeURIComponent(query); + url = '/api/v1/query?query=' + encodeURIComponent(interpolated) + + '&time=' + (moment().valueOf() / 1000); return this._request('GET', url) .then(function(result) { - return _.map(result.data.result, function(metricData) { + return _.map(result.data.data.result, function(metricData) { return { text: getOriginalMetricName(metricData.metric), expandable: true From daee7970f33ad9548f73a3465f10dc9e853fe45e Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 12:29:53 +0100 Subject: [PATCH 03/13] Add label_values query to get labels on a particular metric --- .../datasource/prometheus/datasource.js | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index dd8ba8e2b3b..ea1f4a350e3 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -128,7 +128,7 @@ function (angular, _, moment, dateMath) { }; PrometheusDatasource.prototype.metricFindQuery = function(query) { - var url; + if (!query) { return $q.when([]); } var interpolated; try { @@ -138,27 +138,51 @@ function (angular, _, moment, dateMath) { return $q.reject(err); } - var metricsQuery = interpolated.match(/^[a-zA-Z_:*][a-zA-Z0-9_:*]*/); - var labelValuesQuery = interpolated.match(/^label_values\((.+)\)/); + var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/; + var metric_names_regex = /^metrics\((.+)\)$/; - if (labelValuesQuery) { - // return label values - url = '/api/v1/label/' + labelValuesQuery[1] + '/values'; + var label_values_query = interpolated.match(label_values_regex); + if (label_values_query) { + if (!label_values_query[2]) { + // return label values globally + var url = '/api/v1/label/' + label_values_query[1] + '/values'; - return this._request('GET', url).then(function(result) { - return _.map(result.data.data, function(value) { - return {text: value}; + return this._request('GET', url).then(function(result) { + return _.map(result.data.data, function(value) { + return {text: value}; + }); }); - }); - } else if (metricsQuery != null && metricsQuery[0].indexOf('*') >= 0) { - // if query has wildcard character, return metric name list - url = '/api/v1/label/__name__/values'; + } else { + var metric_query = 'count(' + label_values_query[1] + ') by (' + + label_values_query[2] + ')'; + var url = '/api/v1/query?query=' + encodeURIComponent(metric_query) + + '&time=' + (moment().valueOf() / 1000); + + return this._request('GET', url) + .then(function(result) { + if (result.data.data.result.length === 0 || + _.keys(result.data.data.result[0].metric).length === 0) { + return []; + } + return _.map(result.data.data.result, function(metricValue) { + return { + text: metricValue.metric[label_values_query[2]], + expandable: true + }; + }); + }); + } + } + + var metric_names_query = interpolated.match(metric_names_regex); + if (metric_names_query) { + var url = '/api/v1/label/__name__/values'; return this._request('GET', url) .then(function(result) { return _.chain(result.data.data) .filter(function(metricName) { - var r = new RegExp(metricsQuery[0].replace(/\*/g, '.*')); + var r = new RegExp(metric_names_query[1]); return r.test(metricName); }) .map(function(matchedMetricName) { @@ -171,7 +195,7 @@ function (angular, _, moment, dateMath) { }); } else { // if query contains full metric name, return metric name and label list - url = '/api/v1/query?query=' + encodeURIComponent(interpolated) + + var url = '/api/v1/query?query=' + encodeURIComponent(interpolated) + '&time=' + (moment().valueOf() / 1000); return this._request('GET', url) From 055efa3904a128d93e88922f69daeaa41d6d6a32 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 12:42:48 +0100 Subject: [PATCH 04/13] Add prometheus docs --- docs/mkdocs.yml | 1 + docs/sources/datasources/overview.md | 5 +- docs/sources/datasources/prometheus.md | 65 ++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 docs/sources/datasources/prometheus.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index f44b92645a6..51c68866514 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -66,6 +66,7 @@ pages: - ['datasources/influxdb.md', 'Data Sources', 'InfluxDB'] - ['datasources/opentsdb.md', 'Data Sources', 'OpenTSDB'] - ['datasources/kairosdb.md', 'Data Sources', 'KairosDB'] +- ['datasources/prometheus.md', 'Data Sources', 'Prometheus'] - ['tutorials/index.md', 'Tutorials', 'Tutorials'] - ['tutorials/hubot_howto.md', 'Tutorials', 'How To integrate Hubot and Grafana'] diff --git a/docs/sources/datasources/overview.md b/docs/sources/datasources/overview.md index 0badfdaad3d..99a51c9f751 100644 --- a/docs/sources/datasources/overview.md +++ b/docs/sources/datasources/overview.md @@ -1,7 +1,7 @@ ---- page_title: Data Source Overview page_description: Data Source Overview -page_keywords: grafana, graphite, influxDB, KairosDB, OpenTSDB, documentation +page_keywords: grafana, graphite, influxDB, KairosDB, OpenTSDB, Prometheus, documentation --- # Data Source Overview @@ -18,5 +18,6 @@ The following datasources are officially supported: * [InfluxDB](/datasources/influxdb/) * [OpenTSDB](/datasources/opentsdb/) * [KairosDB](/datasources/kairosdb) +* [Prometheus](/datasources/prometheus) -Grafana can query any Elasticsearch index for annotation events, but at this time, it's not supported for metric queries. Learn more about [annotations](/reference/annotations/#elasticsearch-annotations) +Grafana can query Failcsearch index for annotation events, but at this time, it's not supported for metric queries. Learn more about [annotations](/reference/annotations/#elasticsearch-annotations) diff --git a/docs/sources/datasources/prometheus.md b/docs/sources/datasources/prometheus.md new file mode 100644 index 00000000000..f3069d68a62 --- /dev/null +++ b/docs/sources/datasources/prometheus.md @@ -0,0 +1,65 @@ +---- +page_title: Prometheus query guide +page_description: Prometheus query guide +page_keywords: grafana, prometheus, metrics, query, documentation +--- + +# Prometheus +Grafana includes support for Prometheus Datasources. While the process of adding the datasource is similar to adding a Graphite or OpenTSDB datasource type, Prometheus does have a few different options for building queries. + +## Adding the data source to Grafana +![](/img/v2/add_Prometheus.jpg) + +1. Open the side menu by clicking the the Grafana icon in the top header. +2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. + + > NOTE: If this link is missing in the side menu it means that your current user does not have the `Admin` role for the current organization. + +3. Click the `Add new` link in the top header. +4. Select `Prometheus` from the dropdown. + +Name | Description +------------ | ------------- +Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. +Default | Default data source means that it will be pre-selected for new panels. +Url | The http protocol, ip and port of you Prometheus server (default port is usually 9090) +Access | Proxy = access via Grafana backend, Direct = access directory from browser. +Basic Auth | Enable basic authentication to the Prometheus datasource. +User | Name of your Prometheus user +Password | Database user's password + + > Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the brower. + + > Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source. + +## Query editor +Open a graph in edit mode by click the title. + +![](/img/v2/prometheus_editor.png) + +For details on Prometheus metric queries check out the Prometheus documentation +- [Query Metrics - Prometheus documentation](http://prometheus.io/docs/querying/basics/). + +## Templated queries +Prometheus Datasource Plugin provides the following functions in `Variables values query` field in Templating Editor to query `metric names` and `labels names` on the Prometheus server. + +Name | Description +------- | -------- +`label_values(label)` | Returns a list of label values for the `label` in every metric. +`label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. +`metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. + +For details of `metric names` & `label names`, and `label values`, please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). + +You can create a template variable in Grafana and have that variable filled with values from any Prometheus metric exploration query. +You can then use this variable in your Prometheus metric queries. + +For example you can have a variable that contains all values for label `hostname` if you specify a query like this +in the templating edit view. +```sql +label_values(hostname) +``` + +You can also use raw queries & regular expressions to extract anything you might need. + +![](/img/v2/prometheus_templating.png) From 2e291d73aa9a9034dad21cda59447afa777e4e0e Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 12:46:44 +0100 Subject: [PATCH 05/13] jshint fixes --- public/app/plugins/datasource/prometheus/datasource.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index ea1f4a350e3..75970636642 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -141,11 +141,12 @@ function (angular, _, moment, dateMath) { var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/; var metric_names_regex = /^metrics\((.+)\)$/; + var url; var label_values_query = interpolated.match(label_values_regex); if (label_values_query) { if (!label_values_query[2]) { // return label values globally - var url = '/api/v1/label/' + label_values_query[1] + '/values'; + url = '/api/v1/label/' + label_values_query[1] + '/values'; return this._request('GET', url).then(function(result) { return _.map(result.data.data, function(value) { @@ -155,7 +156,7 @@ function (angular, _, moment, dateMath) { } else { var metric_query = 'count(' + label_values_query[1] + ') by (' + label_values_query[2] + ')'; - var url = '/api/v1/query?query=' + encodeURIComponent(metric_query) + + url = '/api/v1/query?query=' + encodeURIComponent(metric_query) + '&time=' + (moment().valueOf() / 1000); return this._request('GET', url) @@ -176,7 +177,7 @@ function (angular, _, moment, dateMath) { var metric_names_query = interpolated.match(metric_names_regex); if (metric_names_query) { - var url = '/api/v1/label/__name__/values'; + url = '/api/v1/label/__name__/values'; return this._request('GET', url) .then(function(result) { @@ -195,7 +196,7 @@ function (angular, _, moment, dateMath) { }); } else { // if query contains full metric name, return metric name and label list - var url = '/api/v1/query?query=' + encodeURIComponent(interpolated) + + url = '/api/v1/query?query=' + encodeURIComponent(interpolated) + '&time=' + (moment().valueOf() / 1000); return this._request('GET', url) From 59dbe45784f5cf9f334207061828e4a0057ab205 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 13:30:21 +0100 Subject: [PATCH 06/13] Fix typo in docs --- docs/sources/datasources/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/overview.md b/docs/sources/datasources/overview.md index 99a51c9f751..4e1c08fe82b 100644 --- a/docs/sources/datasources/overview.md +++ b/docs/sources/datasources/overview.md @@ -20,4 +20,4 @@ The following datasources are officially supported: * [KairosDB](/datasources/kairosdb) * [Prometheus](/datasources/prometheus) -Grafana can query Failcsearch index for annotation events, but at this time, it's not supported for metric queries. Learn more about [annotations](/reference/annotations/#elasticsearch-annotations) +Grafana can query any Elasticsearch index for annotation events, but at this time, it's not supported for metric queries. Learn more about [annotations](/reference/annotations/#elasticsearch-annotations) From 67f253830fd41ff8a8ff119a9340e3f7380f61f7 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 14:19:17 +0100 Subject: [PATCH 07/13] Add Prometheus metricFindQuery unit tests --- .../test/specs/prometheus-datasource-specs.js | 53 +++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/public/test/specs/prometheus-datasource-specs.js b/public/test/specs/prometheus-datasource-specs.js index c65b8771851..a2f0d8648f5 100644 --- a/public/test/specs/prometheus-datasource-specs.js +++ b/public/test/specs/prometheus-datasource-specs.js @@ -1,9 +1,7 @@ define([ './helpers', 'moment', - 'app/plugins/datasource/prometheus/datasource', - 'app/services/backendSrv', - 'app/services/alertSrv' + 'app/plugins/datasource/prometheus/datasource' ], function(helpers, moment) { 'use strict'; @@ -11,7 +9,6 @@ define([ var ctx = new helpers.ServiceTestContext(); beforeEach(module('grafana.services')); - beforeEach(ctx.providePhase(['templateSrv'])); beforeEach(ctx.createService('PrometheusDatasource')); beforeEach(function() { ctx.ds = new ctx.service({ url: '', user: 'test', password: 'mupp' }); @@ -56,6 +53,54 @@ define([ }); + describe('When performing metricFindQuery', function() { + var results; + var response; + + it('label_values(resource) should generate label search query', function() { + response = { + status: "success", + data: ["value1", "value2", "value3"] + }; + ctx.$httpBackend.expect('GET', '/api/v1/label/resource/values').respond(response); + ctx.ds.metricFindQuery('label_values(resource)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + + it('label_values(metric, resource) should generate count metric query', function() { + response = { + status: "success", + data:{ + resultType: "vector", + result:[ + {metric:{resource:"value1"},value:[]}, + {metric:{resource:"value2"},value:[]}, + {metric:{resource:"value3"},value:[]} + ] + } + }; + ctx.$httpBackend.expect('GET', /\/api\/v1\/query\?query=count\(metric\)%20by%20\(resource\)&time=.*/).respond(response); + ctx.ds.metricFindQuery('label_values(metric, resource)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + + it('metrics(metric.*) should generate metric name query', function() { + response = { + status: "success", + data:["metric1","metric2","metric3","nomatch"] + }; + ctx.$httpBackend.expect('GET', '/api/v1/label/__name__/values').respond(response); + ctx.ds.metricFindQuery('metrics(metric.*)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + + }); }); }); From b90e4057bac30ebcc62b0fe15d9064880a652c08 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 15:52:15 +0100 Subject: [PATCH 08/13] Convert prometheus specs to typescript --- .../prometheus/specs/datasource_specs.ts | 93 +++++++++++++++ .../test/specs/prometheus-datasource-specs.js | 106 ------------------ 2 files changed, 93 insertions(+), 106 deletions(-) create mode 100644 public/app/plugins/datasource/prometheus/specs/datasource_specs.ts delete mode 100644 public/test/specs/prometheus-datasource-specs.js diff --git a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts new file mode 100644 index 00000000000..2e5df8e0bf9 --- /dev/null +++ b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts @@ -0,0 +1,93 @@ +/// +/// + +import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common'; +import moment = require('moment'); +declare var helpers: any; + +describe('PrometheusDatasource', function() { + var ctx = new helpers.ServiceTestContext(); + beforeEach(angularMocks.module('grafana.services')); + beforeEach(ctx.createService('PrometheusDatasource')); + beforeEach(function() { + ctx.ds = new ctx.service({ url: '', user: 'test', password: 'mupp' }); + }); + describe('When querying prometheus with one target using query editor target spec', function() { + var results; + var urlExpected = '/api/v1/query_range?query=' + + encodeURIComponent('test{job="testjob"}') + + '&start=1443438675&end=1443460275&step=60s'; + var query = { + range: { from: moment(1443438674760), to: moment(1443460274760) }, + targets: [{ expr: 'test{job="testjob"}' }], + interval: '60s' + }; + var response = { + status: "success", + data: { + resultType: "matrix", + result: [{ + metric: {"__name__": "test", job: "testjob"}, + values: [[1443454528, "3846"]] + }] + } + }; + beforeEach(function() { + ctx.$httpBackend.expect('GET', urlExpected).respond(response); + ctx.ds.query(query).then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + }); + it('should generate the correct query', function() { + ctx.$httpBackend.verifyNoOutstandingExpectation(); + }); + it('should return series list', function() { + expect(results.data.length).to.be(1); + expect(results.data[0].target).to.be('test{job="testjob"}'); + }); + }); + describe('When performing metricFindQuery', function() { + var results; + var response; + it('label_values(resource) should generate label search query', function() { + response = { + status: "success", + data: ["value1", "value2", "value3"] + }; + ctx.$httpBackend.expect('GET', '/api/v1/label/resource/values').respond(response); + ctx.ds.metricFindQuery('label_values(resource)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + it('label_values(metric, resource) should generate count metric query', function() { + response = { + status: "success", + data: { + resultType: "vector", + result: [ + {metric: {resource: "value1"}, value: []}, + {metric: {resource: "value2"}, value: []}, + {metric: {resource: "value3"}, value: []} + ] + } + }; + ctx.$httpBackend.expect('GET', /\/api\/v1\/query\?query=count\(metric\)%20by%20\(resource\)&time=.*/).respond(response); + ctx.ds.metricFindQuery('label_values(metric, resource)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + it('metrics(metric.*) should generate metric name query', function() { + response = { + status: "success", + data: ["metric1","metric2","metric3","nomatch"] + }; + ctx.$httpBackend.expect('GET', '/api/v1/label/__name__/values').respond(response); + ctx.ds.metricFindQuery('metrics(metric.*)').then(function(data) { results = data; }); + ctx.$httpBackend.flush(); + ctx.$rootScope.$apply(); + expect(results.length).to.be(3); + }); + }); +}); + diff --git a/public/test/specs/prometheus-datasource-specs.js b/public/test/specs/prometheus-datasource-specs.js deleted file mode 100644 index a2f0d8648f5..00000000000 --- a/public/test/specs/prometheus-datasource-specs.js +++ /dev/null @@ -1,106 +0,0 @@ -define([ - './helpers', - 'moment', - 'app/plugins/datasource/prometheus/datasource' -], function(helpers, moment) { - 'use strict'; - - describe('PrometheusDatasource', function() { - var ctx = new helpers.ServiceTestContext(); - - beforeEach(module('grafana.services')); - beforeEach(ctx.createService('PrometheusDatasource')); - beforeEach(function() { - ctx.ds = new ctx.service({ url: '', user: 'test', password: 'mupp' }); - }); - - describe('When querying prometheus with one target using query editor target spec', function() { - var results; - var urlExpected = '/api/v1/query_range?query=' + - encodeURIComponent('test{job="testjob"}') + - '&start=1443438675&end=1443460275&step=60s'; - var query = { - range: { from: moment(1443438674760), to: moment(1443460274760) }, - targets: [{ expr: 'test{job="testjob"}' }], - interval: '60s' - }; - - var response = { - "status":"success", - "data":{ - "resultType":"matrix", - "result":[{ - "metric":{"__name__":"test", "job":"testjob"}, - "values":[[1443454528,"3846"]] - }] - } - }; - - beforeEach(function() { - ctx.$httpBackend.expect('GET', urlExpected).respond(response); - ctx.ds.query(query).then(function(data) { results = data; }); - ctx.$httpBackend.flush(); - }); - - it('should generate the correct query', function() { - ctx.$httpBackend.verifyNoOutstandingExpectation(); - }); - - it('should return series list', function() { - expect(results.data.length).to.be(1); - expect(results.data[0].target).to.be('test{job="testjob"}'); - }); - - }); - - describe('When performing metricFindQuery', function() { - var results; - var response; - - it('label_values(resource) should generate label search query', function() { - response = { - status: "success", - data: ["value1", "value2", "value3"] - }; - ctx.$httpBackend.expect('GET', '/api/v1/label/resource/values').respond(response); - ctx.ds.metricFindQuery('label_values(resource)').then(function(data) { results = data; }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - - it('label_values(metric, resource) should generate count metric query', function() { - response = { - status: "success", - data:{ - resultType: "vector", - result:[ - {metric:{resource:"value1"},value:[]}, - {metric:{resource:"value2"},value:[]}, - {metric:{resource:"value3"},value:[]} - ] - } - }; - ctx.$httpBackend.expect('GET', /\/api\/v1\/query\?query=count\(metric\)%20by%20\(resource\)&time=.*/).respond(response); - ctx.ds.metricFindQuery('label_values(metric, resource)').then(function(data) { results = data; }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - - it('metrics(metric.*) should generate metric name query', function() { - response = { - status: "success", - data:["metric1","metric2","metric3","nomatch"] - }; - ctx.$httpBackend.expect('GET', '/api/v1/label/__name__/values').respond(response); - ctx.ds.metricFindQuery('metrics(metric.*)').then(function(data) { results = data; }); - ctx.$httpBackend.flush(); - ctx.$rootScope.$apply(); - expect(results.length).to.be(3); - }); - - }); - }); -}); - From 0c222c4e8c14b11046ce06f43e5e80f479283d51 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 16:44:16 +0100 Subject: [PATCH 09/13] Fix Prometheus test connection --- docs/sources/index.md | 2 +- public/app/plugins/datasource/prometheus/datasource.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/index.md b/docs/sources/index.md index 6c1c1ce7837..f595c81ef28 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -10,7 +10,7 @@ It provides a powerful and elegant way to create, share, and explore data and da Grafana is most commonly used for Internet infrastructure and application analytics, but many use it in other domains including industrial sensors, home automation, weather, and process control. -Grafana features pluggable panels and data sources allowing easy extensibility. There is currently rich support for [Graphite](http://graphite.readthedocs.org/en/latest/), [InfluxDB](http://influxdb.org) and [OpenTSDB](http://opentsdb.net). There is also experimental support for [KairosDB](https://github.com/kairosdb/kairosdb), and SQL is on the roadmap. Grafana has a variety of panels, including a fully featured graph panel with rich visualization options. +Grafana features pluggable panels and data sources allowing easy extensibility. There is currently rich support for [Graphite](http://graphite.readthedocs.org/en/latest/), [InfluxDB](http://influxdb.org) and [OpenTSDB](http://opentsdb.net). There is also experimental support for [KairosDB](https://github.com/kairosdb/kairosdb), [Prometheus](http://prometheus.io/), and SQL is on the roadmap. Grafana has a variety of panels, including a fully featured graph panel with rich visualization options. Version 2.0 was released in April 2015: Grafana now ships with its own backend server that brings [many changes and features](../guides/whats-new-in-v2/). Version 2.1 was released in July 2015 and added [even more features and enhancements](../guides/whats-new-in-v2-1/). diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index 75970636642..7a4316a528e 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -212,7 +212,7 @@ function (angular, _, moment, dateMath) { }; PrometheusDatasource.prototype.testDatasource = function() { - return this.metricFindQuery('*').then(function() { + return this.metricFindQuery('metrics(.*)').then(function() { return { status: 'success', message: 'Data source is working', title: 'Success' }; }); }; From 7cadb9012a2b6f15a8e38600ae03ed6b7b274e6e Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 30 Sep 2015 16:45:38 +0100 Subject: [PATCH 10/13] Switch to png image --- docs/sources/datasources/prometheus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/prometheus.md b/docs/sources/datasources/prometheus.md index f3069d68a62..e975a5cf8a4 100644 --- a/docs/sources/datasources/prometheus.md +++ b/docs/sources/datasources/prometheus.md @@ -8,7 +8,7 @@ page_keywords: grafana, prometheus, metrics, query, documentation Grafana includes support for Prometheus Datasources. While the process of adding the datasource is similar to adding a Graphite or OpenTSDB datasource type, Prometheus does have a few different options for building queries. ## Adding the data source to Grafana -![](/img/v2/add_Prometheus.jpg) +![](/img/v2/add_Prometheus.png) 1. Open the side menu by clicking the the Grafana icon in the top header. 2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. From d0b744387bd3f546d35ba765c8cf7dffdebb1b7e Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 1 Oct 2015 01:33:13 +0900 Subject: [PATCH 11/13] fix unmatched tag --- .../prometheus/partials/query.editor.html | 62 ++++++------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index 29cb2acd3d7..e2190c93dca 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -41,18 +41,11 @@ Query
  • - - + + +
  • @@ -60,17 +53,10 @@ Metric
  • - - + + +
  • @@ -86,15 +72,10 @@ Legend format
  • - + +
  • @@ -107,25 +88,18 @@ Step
  • - + data-placement="right" spellcheck='false' placeholder="{{target.calculatedInterval}}" data-min-length=0 data-items=100 + ng-model-onblur ng-change="refreshMetricData()"> +
  • Resolution
  • - From 3cc69112c194588af7755904551a82524e50f00d Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Thu, 1 Oct 2015 18:01:09 +0200 Subject: [PATCH 12/13] Fix "Link to Prometheus" button for proxied Prometheus sources. --- pkg/api/frontendsettings.go | 5 +++++ pkg/models/datasource.go | 1 + public/app/plugins/datasource/prometheus/datasource.js | 8 ++------ public/app/plugins/datasource/prometheus/queryCtrl.js | 2 +- .../datasource/prometheus/specs/datasource_specs.ts | 10 +++++----- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 3440ddb3715..cc07b9cfb49 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -81,6 +81,11 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro dsMap["index"] = ds.Database } + if ds.Type == m.DS_PROMETHEUS { + // add unproxied server URL for link to Prometheus web UI + dsMap["directUrl"] = ds.Url + } + datasources[ds.Name] = dsMap } diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 75e2134c09f..504578465fd 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -12,6 +12,7 @@ const ( DS_ES = "elasticsearch" DS_OPENTSDB = "opentsdb" DS_CLOUDWATCH = "cloudwatch" + DS_PROMETHEUS = "prometheus" DS_ACCESS_DIRECT = "direct" DS_ACCESS_PROXY = "proxy" ) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index 7a4316a528e..0c0b660a21f 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -21,12 +21,8 @@ function (angular, _, moment, dateMath) { this.name = datasource.name; this.supportMetrics = true; - var url = datasource.url; - if (url[url.length-1] === '/') { - // remove trailing slash - url = url.substr(0, url.length - 1); - } - this.url = url; + this.url = datasource.url.replace(/\/$/g, ''); + this.directUrl = datasource.directUrl.replace(/\/$/g, ''); this.basicAuth = datasource.basicAuth; this.lastErrors = {}; } diff --git a/public/app/plugins/datasource/prometheus/queryCtrl.js b/public/app/plugins/datasource/prometheus/queryCtrl.js index 08f8e899c68..051c4313e97 100644 --- a/public/app/plugins/datasource/prometheus/queryCtrl.js +++ b/public/app/plugins/datasource/prometheus/queryCtrl.js @@ -111,7 +111,7 @@ function (angular, _, kbn, dateMath) { }; var hash = encodeURIComponent(JSON.stringify([expr])); - return $scope.datasource.url + '/graph#' + hash; + return $scope.datasource.directUrl + '/graph#' + hash; }; $scope.calculateInterval = function() { diff --git a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts index 2e5df8e0bf9..fc43da279fc 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts @@ -10,11 +10,11 @@ describe('PrometheusDatasource', function() { beforeEach(angularMocks.module('grafana.services')); beforeEach(ctx.createService('PrometheusDatasource')); beforeEach(function() { - ctx.ds = new ctx.service({ url: '', user: 'test', password: 'mupp' }); + ctx.ds = new ctx.service({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp' }); }); describe('When querying prometheus with one target using query editor target spec', function() { var results; - var urlExpected = '/api/v1/query_range?query=' + + var urlExpected = 'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=1443438675&end=1443460275&step=60s'; var query = { @@ -53,7 +53,7 @@ describe('PrometheusDatasource', function() { status: "success", data: ["value1", "value2", "value3"] }; - ctx.$httpBackend.expect('GET', '/api/v1/label/resource/values').respond(response); + ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/resource/values').respond(response); ctx.ds.metricFindQuery('label_values(resource)').then(function(data) { results = data; }); ctx.$httpBackend.flush(); ctx.$rootScope.$apply(); @@ -71,7 +71,7 @@ describe('PrometheusDatasource', function() { ] } }; - ctx.$httpBackend.expect('GET', /\/api\/v1\/query\?query=count\(metric\)%20by%20\(resource\)&time=.*/).respond(response); + ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/query\?query=count\(metric\)%20by%20\(resource\)&time=.*/).respond(response); ctx.ds.metricFindQuery('label_values(metric, resource)').then(function(data) { results = data; }); ctx.$httpBackend.flush(); ctx.$rootScope.$apply(); @@ -82,7 +82,7 @@ describe('PrometheusDatasource', function() { status: "success", data: ["metric1","metric2","metric3","nomatch"] }; - ctx.$httpBackend.expect('GET', '/api/v1/label/__name__/values').respond(response); + ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response); ctx.ds.metricFindQuery('metrics(metric.*)').then(function(data) { results = data; }); ctx.$httpBackend.flush(); ctx.$rootScope.$apply(); From 5e19fdb49213c2336196e373e26865aa8bdb66ba Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 2 Oct 2015 12:10:11 +0900 Subject: [PATCH 13/13] fix prometheus time conversion --- public/app/plugins/datasource/prometheus/datasource.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.js b/public/app/plugins/datasource/prometheus/datasource.js index 0c0b660a21f..4fa0048cbab 100644 --- a/public/app/plugins/datasource/prometheus/datasource.js +++ b/public/app/plugins/datasource/prometheus/datasource.js @@ -271,12 +271,6 @@ function (angular, _, moment, dateMath) { function getPrometheusTime(date, roundUp) { if (_.isString(date)) { - if (date === 'now') { - return 'now()'; - } - if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) { - return date.replace('now', 'now()').replace('-', ' - '); - } date = dateMath.parse(date, roundUp); } return (date.valueOf() / 1000).toFixed(0);