mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'prometheus'
This commit is contained in:
commit
a2bf3e056d
2
docker/blocks/prometheus/Dockerfile
Normal file
2
docker/blocks/prometheus/Dockerfile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
FROM prom/prometheus
|
||||||
|
ADD prometheus.yml /etc/prometheus/
|
6
docker/blocks/prometheus/fig
Normal file
6
docker/blocks/prometheus/fig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
prometheus:
|
||||||
|
build: blocks/prometheus
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- /var/docker/prometheus:/prometheus-data
|
30
docker/blocks/prometheus/prometheus.yml
Normal file
30
docker/blocks/prometheus/prometheus.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# my global config
|
||||||
|
global:
|
||||||
|
scrape_interval: 10s # By default, scrape targets every 15 seconds.
|
||||||
|
evaluation_interval: 10s # By default, scrape targets every 15 seconds.
|
||||||
|
# scrape_timeout is set to the global default (10s).
|
||||||
|
|
||||||
|
# Attach these extra labels to all timeseries collected by this Prometheus instance.
|
||||||
|
labels:
|
||||||
|
monitor: 'codelab-monitor'
|
||||||
|
|
||||||
|
# Load and evaluate rules in this file every 'evaluation_interval' seconds.
|
||||||
|
rule_files:
|
||||||
|
# - "first.rules"
|
||||||
|
# - "second.rules"
|
||||||
|
|
||||||
|
# A scrape configuration containing exactly one endpoint to scrape:
|
||||||
|
# Here it's Prometheus itself.
|
||||||
|
scrape_configs:
|
||||||
|
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
|
||||||
|
# Override the global default and scrape targets from this job every 5 seconds.
|
||||||
|
scrape_interval: 10s
|
||||||
|
scrape_timeout: 10s
|
||||||
|
|
||||||
|
# metrics_path defaults to '/metrics'
|
||||||
|
# scheme defaults to 'http'.
|
||||||
|
|
||||||
|
target_groups:
|
||||||
|
- targets: ['localhost:9090', '172.17.42.1:9091']
|
256
public/app/plugins/datasource/prometheus/datasource.js
Normal file
256
public/app/plugins/datasource/prometheus/datasource.js
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'lodash',
|
||||||
|
'kbn',
|
||||||
|
'moment',
|
||||||
|
'app/core/utils/datemath',
|
||||||
|
'./directives',
|
||||||
|
'./queryCtrl',
|
||||||
|
],
|
||||||
|
function (angular, _, kbn, dateMath) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.services');
|
||||||
|
|
||||||
|
module.factory('PrometheusDatasource', function($q, backendSrv, templateSrv) {
|
||||||
|
|
||||||
|
function PrometheusDatasource(datasource) {
|
||||||
|
this.type = 'prometheus';
|
||||||
|
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
||||||
|
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.basicAuth = datasource.basicAuth;
|
||||||
|
this.lastErrors = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype._request = function(method, url) {
|
||||||
|
var options = {
|
||||||
|
url: this.url + url,
|
||||||
|
method: method
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.basicAuth) {
|
||||||
|
options.withCredentials = true;
|
||||||
|
options.headers = {
|
||||||
|
"Authorization": this.basicAuth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return backendSrv.datasourceRequest(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called once per panel (graph)
|
||||||
|
PrometheusDatasource.prototype.query = function(options) {
|
||||||
|
var start = getPrometheusTime(options.range.from, false);
|
||||||
|
var end = getPrometheusTime(options.range.to, true);
|
||||||
|
|
||||||
|
var queries = [];
|
||||||
|
_.each(options.targets, _.bind(function(target) {
|
||||||
|
if (!target.expr || target.hide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = {};
|
||||||
|
query.expr = templateSrv.replace(target.expr, options.scopedVars);
|
||||||
|
|
||||||
|
var interval = target.interval || options.interval;
|
||||||
|
var intervalFactor = target.intervalFactor || 1;
|
||||||
|
query.step = this.calculateInterval(interval, intervalFactor);
|
||||||
|
|
||||||
|
queries.push(query);
|
||||||
|
}, this));
|
||||||
|
|
||||||
|
// No valid targets, return the empty result to save a round trip.
|
||||||
|
if (_.isEmpty(queries)) {
|
||||||
|
var d = $q.defer();
|
||||||
|
d.resolve({ data: [] });
|
||||||
|
return d.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allQueryPromise = _.map(queries, _.bind(function(query) {
|
||||||
|
return this.performTimeSeriesQuery(query, start, end);
|
||||||
|
}, this));
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
return $q.all(allQueryPromise)
|
||||||
|
.then(function(allResponse) {
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
_.each(allResponse, function(response, index) {
|
||||||
|
if (response.status === 'error') {
|
||||||
|
self.lastErrors.query = response.error;
|
||||||
|
throw response.error;
|
||||||
|
}
|
||||||
|
delete self.lastErrors.query;
|
||||||
|
|
||||||
|
_.each(response.data.data.result, function(metricData) {
|
||||||
|
result.push(transformMetricData(metricData, options.targets[index]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data: result };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
||||||
|
var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end;
|
||||||
|
|
||||||
|
var step = query.step;
|
||||||
|
var range = Math.floor(end - start);
|
||||||
|
// Prometheus drop query if range/step > 11000
|
||||||
|
// calibrate step if it is too big
|
||||||
|
if (step !== 0 && range / step > 11000) {
|
||||||
|
step = Math.floor(range / 11000);
|
||||||
|
}
|
||||||
|
url += '&step=' + step;
|
||||||
|
|
||||||
|
return this._request('GET', url);
|
||||||
|
};
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype.performSuggestQuery = function(query) {
|
||||||
|
var url = '/api/v1/label/__name__/values';
|
||||||
|
|
||||||
|
return this._request('GET', url).then(function(result) {
|
||||||
|
var suggestData = _.filter(result.data.data, function(metricName) {
|
||||||
|
return metricName.indexOf(query) !== 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return suggestData;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype.metricFindQuery = function(query) {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
var metricsQuery = query.match(/^[a-zA-Z_:*][a-zA-Z0-9_:*]*/);
|
||||||
|
var labelValuesQuery = query.match(/^label_values\((.+)\)/);
|
||||||
|
|
||||||
|
if (labelValuesQuery) {
|
||||||
|
// return label values
|
||||||
|
url = '/api/v1/label/' + labelValuesQuery[1] + '/values';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
return this._request('GET', url)
|
||||||
|
.then(function(result) {
|
||||||
|
return _.chain(result.data.data)
|
||||||
|
.filter(function(metricName) {
|
||||||
|
var r = new RegExp(metricsQuery[0].replace(/\*/g, '.*'));
|
||||||
|
return r.test(metricName);
|
||||||
|
})
|
||||||
|
.map(function(matchedMetricName) {
|
||||||
|
return {
|
||||||
|
text: matchedMetricName,
|
||||||
|
expandable: true
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// if query contains full metric name, return metric name and label list
|
||||||
|
url = '/api/v1/query?query=' + encodeURIComponent(query);
|
||||||
|
|
||||||
|
return this._request('GET', url)
|
||||||
|
.then(function(result) {
|
||||||
|
return _.map(result.data.result, function(metricData) {
|
||||||
|
return {
|
||||||
|
text: getOriginalMetricName(metricData.metric),
|
||||||
|
expandable: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype.testDatasource = function() {
|
||||||
|
return this.metricFindQuery('*').then(function() {
|
||||||
|
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PrometheusDatasource.prototype.calculateInterval = function(interval, intervalFactor) {
|
||||||
|
var sec = kbn.interval_to_seconds(interval);
|
||||||
|
|
||||||
|
if (sec < 1) {
|
||||||
|
sec = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sec * intervalFactor;
|
||||||
|
};
|
||||||
|
|
||||||
|
function transformMetricData(md, options) {
|
||||||
|
var dps = [],
|
||||||
|
metricLabel = null;
|
||||||
|
|
||||||
|
metricLabel = createMetricLabel(md.metric, options);
|
||||||
|
|
||||||
|
dps = _.map(md.values, function(value) {
|
||||||
|
return [parseFloat(value[1]), value[0] * 1000];
|
||||||
|
});
|
||||||
|
|
||||||
|
return { target: metricLabel, datapoints: dps };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMetricLabel(labelData, options) {
|
||||||
|
if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
|
||||||
|
return getOriginalMetricName(labelData);
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalSettings = _.templateSettings;
|
||||||
|
_.templateSettings = {
|
||||||
|
interpolate: /\{\{(.+?)\}\}/g
|
||||||
|
};
|
||||||
|
|
||||||
|
var template = _.template(templateSrv.replace(options.legendFormat));
|
||||||
|
var metricName;
|
||||||
|
try {
|
||||||
|
metricName = template(labelData);
|
||||||
|
} catch (e) {
|
||||||
|
metricName = '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
_.templateSettings = originalSettings;
|
||||||
|
|
||||||
|
return metricName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginalMetricName(labelData) {
|
||||||
|
var metricName = labelData.__name__ || '';
|
||||||
|
delete labelData.__name__;
|
||||||
|
var labelPart = _.map(_.pairs(labelData), function(label) {
|
||||||
|
return label[0] + '="' + label[1] + '"';
|
||||||
|
}).join(',');
|
||||||
|
return metricName + '{' + labelPart + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PrometheusDatasource;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
13
public/app/plugins/datasource/prometheus/directives.js
Normal file
13
public/app/plugins/datasource/prometheus/directives.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
],
|
||||||
|
function (angular) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.directives');
|
||||||
|
|
||||||
|
module.directive('metricQueryEditorPrometheus', function() {
|
||||||
|
return {controller: 'PrometheusQueryCtrl', templateUrl: 'app/plugins/datasource/prometheus/partials/query.editor.html'};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,4 @@
|
|||||||
|
<div ng-include="httpConfigPartialSrc"></div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
@ -0,0 +1,143 @@
|
|||||||
|
<div class="tight-form">
|
||||||
|
<ul class="tight-form-list pull-right">
|
||||||
|
<li class="tight-form-item small" ng-show="target.datasource">
|
||||||
|
<em>{{target.datasource}}</em>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item">
|
||||||
|
<div class="dropdown">
|
||||||
|
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu pull-right" role="menu">
|
||||||
|
<li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li>
|
||||||
|
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||||
|
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||||
|
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item last">
|
||||||
|
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||||
|
<i class="fa fa-remove"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="tight-form-list">
|
||||||
|
<li class="tight-form-item" style="min-width: 15px; text-align: center">
|
||||||
|
{{target.refId}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="tight-form-item"
|
||||||
|
ng-click="target.hide = !target.hide; get_data();"
|
||||||
|
role="menuitem">
|
||||||
|
<i class="fa fa-eye"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="tight-form-list" role="menu">
|
||||||
|
<li class="tight-form-item" style="width: 94px">
|
||||||
|
Query
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-xxlarge tight-form-input"
|
||||||
|
ng-model="target.expr"
|
||||||
|
spellcheck='false'
|
||||||
|
placeholder="query expression"
|
||||||
|
data-min-length=0 data-items=100
|
||||||
|
ng-model-onblur
|
||||||
|
ng-change="refreshMetricData()"
|
||||||
|
>
|
||||||
|
<a bs-tooltip="target.datasourceErrors.query"
|
||||||
|
style="color: rgb(229, 189, 28)"
|
||||||
|
ng-show="target.datasourceErrors.query">
|
||||||
|
<i class="fa fa-warning"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="tight-form-item">
|
||||||
|
Metric
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-medium tight-form-input"
|
||||||
|
ng-model="target.metric"
|
||||||
|
spellcheck='false'
|
||||||
|
bs-typeahead="suggestMetrics"
|
||||||
|
placeholder="metric name"
|
||||||
|
data-min-length=0 data-items=100
|
||||||
|
>
|
||||||
|
<a bs-tooltip="target.errors.metric"
|
||||||
|
style="color: rgb(229, 189, 28)"
|
||||||
|
ng-show="target.errors.metric">
|
||||||
|
<i class="fa fa-warning"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tight-form">
|
||||||
|
<ul class="tight-form-list" role="menu">
|
||||||
|
<li class="tight-form-item tight-form-align" style="width: 94px">
|
||||||
|
Legend format
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="tight-form-input input-xxlarge"
|
||||||
|
ng-model="target.legendFormat"
|
||||||
|
spellcheck='false'
|
||||||
|
placeholder="legend format"
|
||||||
|
data-min-length=0 data-items=1000
|
||||||
|
ng-model-onblur
|
||||||
|
ng-change="refreshMetricData()"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tight-form">
|
||||||
|
<ul class="tight-form-list" role="menu">
|
||||||
|
<li class="tight-form-item tight-form-align" style="width: 94px">
|
||||||
|
Step
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-mini tight-form-input"
|
||||||
|
ng-model="target.interval"
|
||||||
|
bs-tooltip="'Leave blank for auto handling based on time range and panel width'"
|
||||||
|
data-placement="right"
|
||||||
|
spellcheck='false'
|
||||||
|
placeholder="{{target.calculatedInterval}}"
|
||||||
|
data-min-length=0 data-items=100
|
||||||
|
ng-model-onblur
|
||||||
|
ng-change="refreshMetricData()"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="tight-form-item">
|
||||||
|
Resolution
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<select ng-model="target.intervalFactor"
|
||||||
|
class="tight-form-input input-mini"
|
||||||
|
ng-options="r.factor as r.label for r in resolutions"
|
||||||
|
ng-change="refreshMetricData()">
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="tight-form-item">
|
||||||
|
<a href="{{target.prometheusLink}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
|
||||||
|
<i class="fa fa-share-square-o"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
15
public/app/plugins/datasource/prometheus/plugin.json
Normal file
15
public/app/plugins/datasource/prometheus/plugin.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "datasource",
|
||||||
|
"name": "Prometheus",
|
||||||
|
|
||||||
|
"type": "prometheus",
|
||||||
|
"serviceName": "PrometheusDatasource",
|
||||||
|
|
||||||
|
"module": "app/plugins/datasource/prometheus/datasource",
|
||||||
|
|
||||||
|
"partials": {
|
||||||
|
"config": "app/plugins/datasource/prometheus/partials/config.html"
|
||||||
|
},
|
||||||
|
|
||||||
|
"metrics": true
|
||||||
|
}
|
133
public/app/plugins/datasource/prometheus/queryCtrl.js
Normal file
133
public/app/plugins/datasource/prometheus/queryCtrl.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'lodash',
|
||||||
|
'kbn',
|
||||||
|
'app/core/utils/datemath',
|
||||||
|
],
|
||||||
|
function (angular, _, kbn, dateMath) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.controllers');
|
||||||
|
|
||||||
|
module.controller('PrometheusQueryCtrl', function($scope) {
|
||||||
|
|
||||||
|
$scope.init = function() {
|
||||||
|
$scope.target.errors = validateTarget();
|
||||||
|
$scope.target.datasourceErrors = {};
|
||||||
|
|
||||||
|
if (!$scope.target.expr) {
|
||||||
|
$scope.target.expr = '';
|
||||||
|
}
|
||||||
|
$scope.target.metric = '';
|
||||||
|
|
||||||
|
$scope.resolutions = [
|
||||||
|
{ factor: 1, },
|
||||||
|
{ factor: 2, },
|
||||||
|
{ factor: 3, },
|
||||||
|
{ factor: 5, },
|
||||||
|
{ factor: 10, },
|
||||||
|
];
|
||||||
|
$scope.resolutions = _.map($scope.resolutions, function(r) {
|
||||||
|
r.label = '1/' + r.factor;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
if (!$scope.target.intervalFactor) {
|
||||||
|
$scope.target.intervalFactor = 2; // default resolution is 1/2
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.calculateInterval();
|
||||||
|
$scope.$on('render', function() {
|
||||||
|
$scope.calculateInterval(); // re-calculate interval when time range is updated
|
||||||
|
});
|
||||||
|
$scope.target.prometheusLink = $scope.linkToPrometheus();
|
||||||
|
|
||||||
|
$scope.$on('typeahead-updated', function() {
|
||||||
|
$scope.$apply($scope.inputMetric);
|
||||||
|
$scope.refreshMetricData();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.datasource.lastErrors = {};
|
||||||
|
$scope.$watch('datasource.lastErrors', function() {
|
||||||
|
$scope.target.datasourceErrors = $scope.datasource.lastErrors;
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.refreshMetricData = function() {
|
||||||
|
$scope.target.errors = validateTarget($scope.target);
|
||||||
|
$scope.calculateInterval();
|
||||||
|
$scope.target.prometheusLink = $scope.linkToPrometheus();
|
||||||
|
|
||||||
|
// this does not work so good
|
||||||
|
if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) {
|
||||||
|
$scope.oldTarget = angular.copy($scope.target);
|
||||||
|
$scope.get_data();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.inputMetric = function() {
|
||||||
|
$scope.target.expr += $scope.target.metric;
|
||||||
|
$scope.target.metric = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.moveMetricQuery = function(fromIndex, toIndex) {
|
||||||
|
_.move($scope.panel.targets, fromIndex, toIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.suggestMetrics = function(query, callback) {
|
||||||
|
$scope.datasource
|
||||||
|
.performSuggestQuery(query)
|
||||||
|
.then(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.linkToPrometheus = function() {
|
||||||
|
var from = dateMath.parse($scope.dashboard.time.from, false);
|
||||||
|
var to = dateMath.parse($scope.dashboard.time.to, true);
|
||||||
|
|
||||||
|
if ($scope.panel.timeFrom) {
|
||||||
|
from = dateMath.parseDateMath('-' + $scope.panel.timeFrom, to, false);
|
||||||
|
}
|
||||||
|
if ($scope.panel.timeShift) {
|
||||||
|
from = dateMath.parseDateMath('-' + $scope.panel.timeShift, from, false);
|
||||||
|
to = dateMath.parseDateMath('-' + $scope.panel.timeShift, to, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var range = Math.ceil((to.valueOf()- from.valueOf()) / 1000);
|
||||||
|
|
||||||
|
var endTime = to.format('YYYY-MM-DD HH:MM');
|
||||||
|
|
||||||
|
var step = kbn.interval_to_seconds(this.target.calculatedInterval);
|
||||||
|
if (step !== 0 && range / step > 11000) {
|
||||||
|
step = Math.floor(range / 11000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expr = {
|
||||||
|
expr: $scope.target.expr,
|
||||||
|
range_input: range + 's',
|
||||||
|
end_input: endTime,
|
||||||
|
//step_input: step,
|
||||||
|
step_input: '',
|
||||||
|
stacked: $scope.panel.stack,
|
||||||
|
tab: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var hash = encodeURIComponent(JSON.stringify([expr]));
|
||||||
|
return $scope.datasource.url + '/graph#' + hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
$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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: validate target
|
||||||
|
function validateTarget() {
|
||||||
|
var errs = {};
|
||||||
|
|
||||||
|
return errs;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
61
public/test/specs/prometheus-datasource-specs.js
Normal file
61
public/test/specs/prometheus-datasource-specs.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
define([
|
||||||
|
'./helpers',
|
||||||
|
'moment',
|
||||||
|
'app/plugins/datasource/prometheus/datasource',
|
||||||
|
'app/services/backendSrv',
|
||||||
|
'app/services/alertSrv'
|
||||||
|
], function(helpers, moment) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('PrometheusDatasource', function() {
|
||||||
|
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' });
|
||||||
|
});
|
||||||
|
|
||||||
|
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=60';
|
||||||
|
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"}');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -63,6 +63,7 @@ module.exports = function(config,grunt) {
|
|||||||
'app/plugins/datasource/grafana/datasource',
|
'app/plugins/datasource/grafana/datasource',
|
||||||
'app/plugins/datasource/graphite/datasource',
|
'app/plugins/datasource/graphite/datasource',
|
||||||
'app/plugins/datasource/influxdb/datasource',
|
'app/plugins/datasource/influxdb/datasource',
|
||||||
|
'app/plugins/datasource/prometheus/datasource',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user