| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- import _ from 'lodash';
- import { dateMath } from '@grafana/data';
- import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version';
- import gfunc from './gfunc';
- import { IQService } from 'angular';
- import { BackendSrv } from 'app/core/services/backend_srv';
- import { TemplateSrv } from 'app/features/templating/template_srv';
- import { ScopedVars } from '@grafana/ui';
- export class GraphiteDatasource {
- basicAuth: string;
- url: string;
- name: string;
- graphiteVersion: any;
- supportsTags: boolean;
- cacheTimeout: any;
- withCredentials: boolean;
- funcDefs: any = null;
- funcDefsPromise: Promise<any> = null;
- _seriesRefLetters: string;
- /** @ngInject */
- constructor(
- instanceSettings: any,
- private $q: IQService,
- private backendSrv: BackendSrv,
- private templateSrv: TemplateSrv
- ) {
- this.basicAuth = instanceSettings.basicAuth;
- this.url = instanceSettings.url;
- this.name = instanceSettings.name;
- this.graphiteVersion = instanceSettings.jsonData.graphiteVersion || '0.9';
- this.supportsTags = supportsTags(this.graphiteVersion);
- this.cacheTimeout = instanceSettings.cacheTimeout;
- this.withCredentials = instanceSettings.withCredentials;
- this.funcDefs = null;
- this.funcDefsPromise = null;
- this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
- }
- getQueryOptionsInfo() {
- return {
- maxDataPoints: true,
- cacheTimeout: true,
- links: [
- {
- text: 'Help',
- url: 'http://docs.grafana.org/features/datasources/graphite/#using-graphite-in-grafana',
- },
- ],
- };
- }
- query(options: any) {
- const graphOptions = {
- from: this.translateTime(options.rangeRaw.from, false, options.timezone),
- until: this.translateTime(options.rangeRaw.to, true, options.timezone),
- targets: options.targets,
- format: options.format,
- cacheTimeout: options.cacheTimeout || this.cacheTimeout,
- maxDataPoints: options.maxDataPoints,
- };
- const params = this.buildGraphiteParams(graphOptions, options.scopedVars);
- if (params.length === 0) {
- return this.$q.when({ data: [] });
- }
- const httpOptions: any = {
- method: 'POST',
- url: '/render',
- data: params.join('&'),
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- };
- this.addTracingHeaders(httpOptions, options);
- if (options.panelId) {
- httpOptions.requestId = this.name + '.panelId.' + options.panelId;
- }
- return this.doGraphiteRequest(httpOptions).then(this.convertDataPointsToMs);
- }
- addTracingHeaders(httpOptions: { headers: any }, options: { dashboardId: any; panelId: any }) {
- const proxyMode = !this.url.match(/^http/);
- if (proxyMode) {
- httpOptions.headers['X-Dashboard-Id'] = options.dashboardId;
- httpOptions.headers['X-Panel-Id'] = options.panelId;
- }
- }
- convertDataPointsToMs(result: any) {
- if (!result || !result.data) {
- return [];
- }
- for (let i = 0; i < result.data.length; i++) {
- const series = result.data[i];
- for (let y = 0; y < series.datapoints.length; y++) {
- series.datapoints[y][1] *= 1000;
- }
- }
- return result;
- }
- parseTags(tagString: string) {
- let tags: string[] = [];
- tags = tagString.split(',');
- if (tags.length === 1) {
- tags = tagString.split(' ');
- if (tags[0] === '') {
- tags = [];
- }
- }
- return tags;
- }
- annotationQuery(options: { annotation: { target: string; tags: string }; rangeRaw: any }) {
- // Graphite metric as annotation
- if (options.annotation.target) {
- const target = this.templateSrv.replace(options.annotation.target, {}, 'glob');
- const graphiteQuery = {
- rangeRaw: options.rangeRaw,
- targets: [{ target: target }],
- format: 'json',
- maxDataPoints: 100,
- };
- return this.query(graphiteQuery).then((result: { data: any[] }) => {
- const list = [];
- for (let i = 0; i < result.data.length; i++) {
- const target = result.data[i];
- for (let y = 0; y < target.datapoints.length; y++) {
- const datapoint = target.datapoints[y];
- if (!datapoint[0]) {
- continue;
- }
- list.push({
- annotation: options.annotation,
- time: datapoint[1],
- title: target.target,
- });
- }
- }
- return list;
- });
- } else {
- // Graphite event as annotation
- const tags = this.templateSrv.replace(options.annotation.tags);
- return this.events({ range: options.rangeRaw, tags: tags }).then((results: any) => {
- const list = [];
- for (let i = 0; i < results.data.length; i++) {
- const e = results.data[i];
- let tags = e.tags;
- if (_.isString(e.tags)) {
- tags = this.parseTags(e.tags);
- }
- list.push({
- annotation: options.annotation,
- time: e.when * 1000,
- title: e.what,
- tags: tags,
- text: e.data,
- });
- }
- return list;
- });
- }
- }
- events(options: { range: any; tags: any; timezone?: any }) {
- try {
- let tags = '';
- if (options.tags) {
- tags = '&tags=' + options.tags;
- }
- return this.doGraphiteRequest({
- method: 'GET',
- url:
- '/events/get_data?from=' +
- this.translateTime(options.range.from, false, options.timezone) +
- '&until=' +
- this.translateTime(options.range.to, true, options.timezone) +
- tags,
- });
- } catch (err) {
- return this.$q.reject(err);
- }
- }
- targetContainsTemplate(target: { target: any }) {
- return this.templateSrv.variableExists(target.target);
- }
- translateTime(date: any, roundUp: any, timezone: any) {
- if (_.isString(date)) {
- if (date === 'now') {
- return 'now';
- } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) {
- date = date.substring(3);
- date = date.replace('m', 'min');
- date = date.replace('M', 'mon');
- return date;
- }
- date = dateMath.parse(date, roundUp, timezone);
- }
- // graphite' s from filter is exclusive
- // here we step back one minute in order
- // to guarantee that we get all the data that
- // exists for the specified range
- if (roundUp) {
- if (date.get('s')) {
- date.add(1, 'm');
- }
- } else if (roundUp === false) {
- if (date.get('s')) {
- date.subtract(1, 'm');
- }
- }
- return date.unix();
- }
- metricFindQuery(query: string, optionalOptions: any) {
- const options: any = optionalOptions || {};
- const interpolatedQuery = this.templateSrv.replace(query);
- // special handling for tag_values(<tag>[,<expression>]*), this is used for template variables
- let matches = interpolatedQuery.match(/^tag_values\(([^,]+)((, *[^,]+)*)\)$/);
- if (matches) {
- const expressions = [];
- const exprRegex = /, *([^,]+)/g;
- let match = exprRegex.exec(matches[2]);
- while (match !== null) {
- expressions.push(match[1]);
- match = exprRegex.exec(matches[2]);
- }
- options.limit = 10000;
- return this.getTagValuesAutoComplete(expressions, matches[1], undefined, options);
- }
- // special handling for tags(<expression>[,<expression>]*), this is used for template variables
- matches = interpolatedQuery.match(/^tags\(([^,]*)((, *[^,]+)*)\)$/);
- if (matches) {
- const expressions = [];
- if (matches[1]) {
- expressions.push(matches[1]);
- const exprRegex = /, *([^,]+)/g;
- let match = exprRegex.exec(matches[2]);
- while (match !== null) {
- expressions.push(match[1]);
- match = exprRegex.exec(matches[2]);
- }
- }
- options.limit = 10000;
- return this.getTagsAutoComplete(expressions, undefined, options);
- }
- const httpOptions: any = {
- method: 'POST',
- url: '/metrics/find',
- params: {},
- data: `query=${interpolatedQuery}`,
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- // for cancellations
- requestId: options.requestId,
- };
- if (options.range) {
- httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);
- httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);
- }
- return this.doGraphiteRequest(httpOptions).then((results: any) => {
- return _.map(results.data, metric => {
- return {
- text: metric.text,
- expandable: metric.expandable ? true : false,
- };
- });
- });
- }
- getTags(optionalOptions: any) {
- const options = optionalOptions || {};
- const httpOptions: any = {
- method: 'GET',
- url: '/tags',
- // for cancellations
- requestId: options.requestId,
- };
- if (options.range) {
- httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);
- httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);
- }
- return this.doGraphiteRequest(httpOptions).then((results: any) => {
- return _.map(results.data, tag => {
- return {
- text: tag.tag,
- id: tag.id,
- };
- });
- });
- }
- getTagValues(tag: string, optionalOptions: any) {
- const options = optionalOptions || {};
- const httpOptions: any = {
- method: 'GET',
- url: '/tags/' + this.templateSrv.replace(tag),
- // for cancellations
- requestId: options.requestId,
- };
- if (options.range) {
- httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);
- httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);
- }
- return this.doGraphiteRequest(httpOptions).then((results: any) => {
- if (results.data && results.data.values) {
- return _.map(results.data.values, value => {
- return {
- text: value.value,
- id: value.id,
- };
- });
- } else {
- return [];
- }
- });
- }
- getTagsAutoComplete(expressions: any[], tagPrefix: any, optionalOptions: any) {
- const options = optionalOptions || {};
- const httpOptions: any = {
- method: 'GET',
- url: '/tags/autoComplete/tags',
- params: {
- expr: _.map(expressions, expression => this.templateSrv.replace((expression || '').trim())),
- },
- // for cancellations
- requestId: options.requestId,
- };
- if (tagPrefix) {
- httpOptions.params.tagPrefix = tagPrefix;
- }
- if (options.limit) {
- httpOptions.params.limit = options.limit;
- }
- if (options.range) {
- httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);
- httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);
- }
- return this.doGraphiteRequest(httpOptions).then((results: any) => {
- if (results.data) {
- return _.map(results.data, tag => {
- return { text: tag };
- });
- } else {
- return [];
- }
- });
- }
- getTagValuesAutoComplete(expressions: any[], tag: any, valuePrefix: any, optionalOptions: any) {
- const options = optionalOptions || {};
- const httpOptions: any = {
- method: 'GET',
- url: '/tags/autoComplete/values',
- params: {
- expr: _.map(expressions, expression => this.templateSrv.replace((expression || '').trim())),
- tag: this.templateSrv.replace((tag || '').trim()),
- },
- // for cancellations
- requestId: options.requestId,
- };
- if (valuePrefix) {
- httpOptions.params.valuePrefix = valuePrefix;
- }
- if (options.limit) {
- httpOptions.params.limit = options.limit;
- }
- if (options.range) {
- httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone);
- httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone);
- }
- return this.doGraphiteRequest(httpOptions).then((results: any) => {
- if (results.data) {
- return _.map(results.data, value => {
- return { text: value };
- });
- } else {
- return [];
- }
- });
- }
- getVersion(optionalOptions: any) {
- const options = optionalOptions || {};
- const httpOptions = {
- method: 'GET',
- url: '/version',
- requestId: options.requestId,
- };
- return this.doGraphiteRequest(httpOptions)
- .then((results: any) => {
- if (results.data) {
- const semver = new SemVersion(results.data);
- return semver.isValid() ? results.data : '';
- }
- return '';
- })
- .catch(() => {
- return '';
- });
- }
- createFuncInstance(funcDef: any, options?: any) {
- return gfunc.createFuncInstance(funcDef, options, this.funcDefs);
- }
- getFuncDef(name: string) {
- return gfunc.getFuncDef(name, this.funcDefs);
- }
- waitForFuncDefsLoaded() {
- return this.getFuncDefs();
- }
- getFuncDefs() {
- if (this.funcDefsPromise !== null) {
- return this.funcDefsPromise;
- }
- if (!supportsFunctionIndex(this.graphiteVersion)) {
- this.funcDefs = gfunc.getFuncDefs(this.graphiteVersion);
- this.funcDefsPromise = Promise.resolve(this.funcDefs);
- return this.funcDefsPromise;
- }
- const httpOptions = {
- method: 'GET',
- url: '/functions',
- };
- this.funcDefsPromise = this.doGraphiteRequest(httpOptions)
- .then((results: any) => {
- if (results.status !== 200 || typeof results.data !== 'object') {
- this.funcDefs = gfunc.getFuncDefs(this.graphiteVersion);
- } else {
- this.funcDefs = gfunc.parseFuncDefs(results.data);
- }
- return this.funcDefs;
- })
- .catch((err: any) => {
- console.log('Fetching graphite functions error', err);
- this.funcDefs = gfunc.getFuncDefs(this.graphiteVersion);
- return this.funcDefs;
- });
- return this.funcDefsPromise;
- }
- testDatasource() {
- const query = {
- panelId: 3,
- rangeRaw: { from: 'now-1h', to: 'now' },
- targets: [{ target: 'constantLine(100)' }],
- maxDataPoints: 300,
- };
- return this.query(query).then(() => {
- return { status: 'success', message: 'Data source is working' };
- });
- }
- doGraphiteRequest(options: {
- method?: string;
- url: any;
- requestId?: any;
- withCredentials?: any;
- headers?: any;
- inspect?: any;
- }) {
- if (this.basicAuth || this.withCredentials) {
- options.withCredentials = true;
- }
- if (this.basicAuth) {
- options.headers = options.headers || {};
- options.headers.Authorization = this.basicAuth;
- }
- options.url = this.url + options.url;
- options.inspect = { type: 'graphite' };
- return this.backendSrv.datasourceRequest(options);
- }
- buildGraphiteParams(options: any, scopedVars: ScopedVars) {
- const graphiteOptions = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout'];
- const cleanOptions = [],
- targets: any = {};
- let target, targetValue, i;
- const regex = /\#([A-Z])/g;
- const intervalFormatFixRegex = /'(\d+)m'/gi;
- let hasTargets = false;
- options['format'] = 'json';
- function fixIntervalFormat(match: any) {
- return match.replace('m', 'min').replace('M', 'mon');
- }
- for (i = 0; i < options.targets.length; i++) {
- target = options.targets[i];
- if (!target.target) {
- continue;
- }
- if (!target.refId) {
- target.refId = this._seriesRefLetters[i];
- }
- targetValue = this.templateSrv.replace(target.target, scopedVars);
- targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat);
- targets[target.refId] = targetValue;
- }
- function nestedSeriesRegexReplacer(match: any, g1: string | number) {
- return targets[g1] || match;
- }
- for (i = 0; i < options.targets.length; i++) {
- target = options.targets[i];
- if (!target.target) {
- continue;
- }
- targetValue = targets[target.refId];
- targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer);
- targets[target.refId] = targetValue;
- if (!target.hide) {
- hasTargets = true;
- cleanOptions.push('target=' + encodeURIComponent(targetValue));
- }
- }
- _.each(options, (value, key) => {
- if (_.indexOf(graphiteOptions, key) === -1) {
- return;
- }
- if (value) {
- cleanOptions.push(key + '=' + encodeURIComponent(value));
- }
- });
- if (!hasTargets) {
- return [];
- }
- return cleanOptions;
- }
- }
- function supportsTags(version: string): boolean {
- return isVersionGtOrEq(version, '1.1');
- }
- function supportsFunctionIndex(version: string): boolean {
- return isVersionGtOrEq(version, '1.1');
- }
|