diff --git a/CHANGELOG.md b/CHANGELOG.md index f00957dc9a8..a67f45e69ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,20 @@ -# 3.0.0-beta6 (unreleased) +# 3.0.0-beta7 (2016-05-02) + +### Bug fixes +* **Dashboard title**: Fixed max dashboard title width (media query) for large screens, fixes [#4859](https://github.com/grafana/grafana/issues/4859) +* **Annotations**: Fixed issue with entering annotation edit view, fixes [#4857](https://github.com/grafana/grafana/issues/4857) +* **Remove query**: Fixed issue with removing query for data sources without collapsable query editors, fixes [#4856](https://github.com/grafana/grafana/issues/4856) +* **Graphite PNG*: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864) +* **InfluxDB**: Fixed issue missing plus group by iconn, fixes [#4862](https://github.com/grafana/grafana/issues/4862) + +### Enhancements +* **InfluxDB**: Added new functions moving_average and difference to query editor, closes [#4698](https://github.com/grafana/grafana/issues/4698) + +# 3.0.0-beta6 (2016-04-29) ### Enhancements * **Singlestat**: Support for gauges in singlestat panel. closes [#3688](https://github.com/grafana/grafana/pull/3688) +* **Templating**: Support for data source as variable, closes [#816](https://github.com/grafana/grafana/pull/816) ### Bug fixes * **InfluxDB 0.12**: Fixed issue templating and `show tag values` query only returning tags for first measurement, fixes [#4726](https://github.com/grafana/grafana/issues/4726) diff --git a/docker/blocks/influxdb/fig b/docker/blocks/influxdb/fig index 931f8a2640a..c537a74b003 100644 --- a/docker/blocks/influxdb/fig +++ b/docker/blocks/influxdb/fig @@ -1,5 +1,5 @@ influxdb: - image: tutum/influxdb:latest + image: tutum/influxdb:0.12 ports: - "2004:2004" - "8083:8083" diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 231b4a601ac..cac6a7c92b8 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -11,7 +11,7 @@ page_keywords: grafana, installation, debian, ubuntu, guide Description | Download ------------ | ------------- Stable .deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb) -Beta .deb for Debian-based Linux | [grafana_3.0.0-beta51460725904_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb) +Beta .deb for Debian-based Linux | [grafana_3.0.0-beta71462173753_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta71462173753_amd64.deb) ## Install Stable @@ -21,9 +21,9 @@ Beta .deb for Debian-based Linux | [grafana_3.0.0-beta51460725904_amd64.deb](h ## Install 3.0 Beta - $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta71462173753_amd64.deb $ sudo apt-get install -y adduser libfontconfig - $ sudo dpkg -i grafana_3.0.0-beta51460725904_amd64.deb + $ sudo dpkg -i grafana_3.0.0-beta71462173753_amd64.deb ## APT Repository diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index d981a5c4b2c..744cafe93db 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -11,7 +11,7 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide Description | Download ------------ | ------------- Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm) -Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta51460725904.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904ยง.x86_64.rpm) +Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta71462173753.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta71462173753.x86_64.rpm) ## Install Stable Release from package file @@ -34,18 +34,18 @@ Or install manually using `rpm`. You can install Grafana using Yum directly. - $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904.x86_64.rpm + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta71462173753.x86_64.rpm Or install manually using `rpm`. #### On CentOS / Fedora / Redhat: $ sudo yum install initscripts fontconfig - $ sudo rpm -Uvh grafana-3.0.0-beta51460725904.x86_64.rpm + $ sudo rpm -Uvh grafana-3.0.0-beta71462173753.x86_64.rpm #### On OpenSuse: - $ sudo rpm -i --nodeps grafana-3.0.0-beta51460725904.x86_64.rpm + $ sudo rpm -i --nodeps grafana-3.0.0-beta71462173753.x86_64.rpm ## Install via YUM Repository diff --git a/latest.json b/latest.json index 9354661781a..8ddb446ec44 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { "stable": "2.6.0", - "testing": "3.0.0-beta5" + "testing": "3.0.0-beta7" } diff --git a/package.json b/package.json index e314476f999..5c2b8471626 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "3.0.0-beta6", + "version": "3.0.0-beta7", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts index b221c8577fa..d370aab9d6d 100644 --- a/public/app/core/components/info_popover.ts +++ b/public/app/core/components/info_popover.ts @@ -11,7 +11,6 @@ export function infoPopover() { template: '', transclude: true, link: function(scope, elem, attrs, ctrl, transclude) { - var offset = attrs.offset || '0 -10px'; var position = attrs.position || 'right middle'; var classes = 'drop-help drop-hide-out-of-bounds'; @@ -39,6 +38,7 @@ export function infoPopover() { position: position, classes: classes, openOn: openOn, + hoverOpenDelay: 400, tetherOptions: { offset: offset } diff --git a/public/app/core/services/datasource_srv.js b/public/app/core/services/datasource_srv.js index 07e3f004d45..32bc9a39725 100644 --- a/public/app/core/services/datasource_srv.js +++ b/public/app/core/services/datasource_srv.js @@ -7,36 +7,11 @@ define([ function (angular, _, coreModule, config) { 'use strict'; - coreModule.default.service('datasourceSrv', function($q, $injector, $rootScope) { + coreModule.default.service('datasourceSrv', function($q, $injector, $rootScope, templateSrv) { var self = this; this.init = function() { this.datasources = {}; - this.metricSources = []; - this.annotationSources = []; - - _.each(config.datasources, function(value, key) { - if (value.meta && value.meta.metrics) { - self.metricSources.push({ - value: key === config.defaultDatasource ? null : key, - name: key, - meta: value.meta, - }); - } - if (value.meta && value.meta.annotations) { - self.annotationSources.push(value); - } - }); - - this.metricSources.sort(function(a, b) { - if (a.meta.builtIn || a.name > b.name) { - return 1; - } - if (a.name < b.name) { - return -1; - } - return 0; - }); }; this.get = function(name) { @@ -44,6 +19,8 @@ function (angular, _, coreModule, config) { return this.get(config.defaultDatasource); } + name = templateSrv.replace(name); + if (this.datasources[name]) { return $q.when(this.datasources[name]); } @@ -89,11 +66,61 @@ function (angular, _, coreModule, config) { }; this.getAnnotationSources = function() { - return this.annotationSources; + return _.reduce(config.datasources, function(memo, value) { + + if (value.meta && value.meta.annotations) { + memo.push(value); + } + + return memo; + }, []); }; - this.getMetricSources = function() { - return this.metricSources; + this.getMetricSources = function(options) { + var metricSources = []; + + _.each(config.datasources, function(value, key) { + if (value.meta && value.meta.metrics) { + metricSources.push({ + value: key === config.defaultDatasource ? null : key, + name: key, + meta: value.meta, + }); + } + }); + + if (!options || !options.skipVariables) { + // look for data source variables + for (var i = 0; i < templateSrv.variables.length; i++) { + var variable = templateSrv.variables[i]; + if (variable.type !== 'datasource') { + continue; + } + + var first = variable.current.value; + var ds = config.datasources[first]; + + if (ds) { + metricSources.push({ + name: '$' + variable.name, + value: '$' + variable.name, + meta: ds.meta, + }); + } + } + } + + metricSources.sort(function(a, b) { + if (a.meta.builtIn || a.name > b.name) { + return 1; + } + if (a.name < b.name) { + return -1; + } + return 0; + }); + + return metricSources; }; this.init(); diff --git a/public/app/core/services/segment_srv.js b/public/app/core/services/segment_srv.js index a0b85a8bfb2..d05a2bb011f 100644 --- a/public/app/core/services/segment_srv.js +++ b/public/app/core/services/segment_srv.js @@ -19,7 +19,7 @@ function (angular, _, coreModule) { if (_.isString(options)) { this.value = options; - this.html = $sce.trustAsHtml(this.value); + this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); return; } diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js index 89c09ac551a..2e1cd1acbf0 100644 --- a/public/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -184,6 +184,7 @@ function (angular, $, _, moment) { p.formatDate = function(date, format) { date = moment.isMoment(date) ? date : moment(date); format = format || 'YYYY-MM-DD HH:mm:ss'; + this.timezone = this.getTimezone(); return this.timezone === 'browser' ? moment(date).format(format) : diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 2d5b66cb13c..99b113b88c5 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -172,7 +172,7 @@ export class DashNavCtrl { var clone = $scope.dashboard.getSaveModelClone(); var blob = new Blob([angular.toJson(clone, true)], { type: "application/json;charset=utf-8" }); var wnd: any = window; - wnd.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime()); + wnd.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime() + '.json'); }; $scope.snapshot = function() { diff --git a/public/app/features/panel/metrics_ds_selector.ts b/public/app/features/panel/metrics_ds_selector.ts index 0c3c61955e3..b281c43048b 100644 --- a/public/app/features/panel/metrics_ds_selector.ts +++ b/public/app/features/panel/metrics_ds_selector.ts @@ -63,6 +63,10 @@ export class MetricsDsSelectorCtrl { } } + if (!this.current) { + this.current = {name: dsValue + ' not found', value: null}; + } + this.dsSegment = uiSegmentSrv.newSegment(this.current.name); } diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 9ff63785273..0bccee8ff35 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -15,6 +15,7 @@ class MetricsPanelCtrl extends PanelCtrl { error: boolean; loading: boolean; datasource: any; + datasourceName: any; $q: any; $timeout: any; datasourceSrv: any; @@ -244,6 +245,7 @@ class MetricsPanelCtrl extends PanelCtrl { } this.panel.datasource = datasource.value; + this.datasourceName = datasource.name; this.datasource = null; this.refresh(); } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 76d348d9b1f..82e19f8681b 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -44,8 +44,8 @@ export class PanelCtrl { this.pluginName = plugin.name; } - $scope.$on("refresh", this.refresh.bind(this)); - $scope.$on("render", this.render.bind(this)); + $scope.$on("refresh", () => this.refresh()); + $scope.$on("render", () => this.render()); $scope.$on("$destroy", () => this.events.emit('panel-teardown')); } diff --git a/public/app/features/panel/query_editor_row.ts b/public/app/features/panel/query_editor_row.ts index 84cc597e388..76bdd6d8539 100644 --- a/public/app/features/panel/query_editor_row.ts +++ b/public/app/features/panel/query_editor_row.ts @@ -79,7 +79,10 @@ export class QueryRowCtrl { } removeQuery() { - delete this.panelCtrl.__collapsedQueryCache[this.target.refId]; + if (this.panelCtrl.__collapsedQueryCache) { + delete this.panelCtrl.__collapsedQueryCache[this.target.refId]; + } + this.panel.targets = _.without(this.panel.targets, this.target); this.panelCtrl.refresh(); } diff --git a/public/app/features/templating/editorCtrl.js b/public/app/features/templating/editorCtrl.js index 291915ee99f..43bda1db05f 100644 --- a/public/app/features/templating/editorCtrl.js +++ b/public/app/features/templating/editorCtrl.js @@ -20,6 +20,13 @@ function (angular, _) { multi: false, }; + $scope.variableTypes = [ + {value: "query", text: "Query"}, + {value: "interval", text: "Interval"}, + {value: "datasource", text: "Data source"}, + {value: "custom", text: "Custom"}, + ]; + $scope.refreshOptions = [ {value: 0, text: "Never"}, {value: 1, text: "On Dashboard Load"}, @@ -35,10 +42,16 @@ function (angular, _) { $scope.init = function() { $scope.mode = 'list'; + $scope.datasourceTypes = {}; $scope.datasources = _.filter(datasourceSrv.getMetricSources(), function(ds) { + $scope.datasourceTypes[ds.meta.id] = {text: ds.meta.name, value: ds.meta.id}; return !ds.meta.builtIn; }); + $scope.datasourceTypes = _.map($scope.datasourceTypes, function(value) { + return value; + }); + $scope.variables = templateSrv.variables; $scope.reset(); @@ -132,9 +145,16 @@ function (angular, _) { if ($scope.current.type === 'interval') { $scope.current.query = '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d'; } + if ($scope.current.type === 'query') { $scope.current.query = ''; } + + if ($scope.current.type === 'datasource') { + $scope.current.query = $scope.datasourceTypes[0].value; + $scope.current.regex = ''; + $scope.current.refresh = 1; + } }; $scope.removeVariable = function(variable) { diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index c7dfe61f2b4..fc387ff44cf 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -75,39 +75,49 @@
- Name - -
-
- Type -
- -
+ Name +
- Data source -
- + + Type + +
+
Query
+
Variable values are fetched from a metric names query to a data source
+
Interval
+
Timespan variable type
+
Datasource
+
Dynamically switch data sources using this type of variable
+
Custom
+
Define variable values manually
+
+ Templating docs +
+
+
+
-
+
- Label - + Label +
-
- Hide -
- +
+ Hide +
+
-
Value Options
+
Interval Options
+
Values @@ -135,6 +145,7 @@
+
Custom Options
Values seperated by comma @@ -142,43 +153,69 @@
-
- Query - - - - - - - - - - - - - - - - - - -
-
- - Regex - Optional, if you want to extract part of a series name or metric node segment - - -
-
- Refresh - - When to update the values of this variable, will slow down dashboard load / time change -
-
+
Query Options
-
-
Selection Options
+
+
+ Data source +
+ +
+
+
+ + Refresh + + When to update the values of this variable. + + +
+ +
+
+
+
+ Query + +
+
+ + Regex + + Optional, if you want to extract part of a series name or metric node segment. + + + +
+
+ +
+
Data source options
+ +
+ +
+ +
+
+ +
+ + +
+
+ +
+
Selection Options
- +
diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 515cd490937..77d44be22e0 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -31,7 +31,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv } if (options.format === 'png') { - return $q.when(this.url + '/render' + '?' + params.join('&')); + return $q.when({data: this.url + '/render' + '?' + params.join('&')}); } var httpOptions: any = {method: this.render_method, url: '/render'}; diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index c2efe3093f4..c3a52531ef1 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -70,6 +70,10 @@
+
+ +
+
diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts index 922440bd482..63d70be5653 100644 --- a/public/app/plugins/datasource/influxdb/query_part.ts +++ b/public/app/plugins/datasource/influxdb/query_part.ts @@ -239,6 +239,24 @@ QueryPartDef.register({ renderer: functionRenderer, }); +QueryPartDef.register({ + type: 'difference', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [], + defaultParams: [], + renderer: functionRenderer, +}); + +QueryPartDef.register({ + type: 'moving_average', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [{ name: "window", type: "number", options: [5, 10, 20, 30, 40]}], + defaultParams: [10], + renderer: functionRenderer, +}); + QueryPartDef.register({ type: 'stddev', addStrategy: addTransformationStrategy, diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 21d8c93aa94..ca7ef051ba0 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -63,11 +63,14 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS var end = getPrometheusTime(options.range.to, true); var queries = []; + var activeTargets = []; + options = _.clone(options); _.each(options.targets, _.bind(function(target) { if (!target.expr || target.hide) { return; } + activeTargets.push(target); var query: any = {}; query.expr = templateSrv.replace(target.expr, options.scopedVars, interpolateQueryExpr); @@ -109,7 +112,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS delete self.lastErrors.query; _.each(response.data.data.result, function(metricData) { - result.push(self.transformMetricData(metricData, options.targets[index], start, end)); + result.push(self.transformMetricData(metricData, activeTargets[index], start, end)); }); }); diff --git a/public/sass/_old_responsive.scss b/public/sass/_old_responsive.scss index a388f9ae26e..a86d65a01e8 100644 --- a/public/sass/_old_responsive.scss +++ b/public/sass/_old_responsive.scss @@ -78,7 +78,7 @@ } } - .dashnav-dashboards-btn a { + .page-dashboard .navbar-page-btn { max-width: none; } .gf-timepicker-nav-btn { diff --git a/public/sass/base/_code.scss b/public/sass/base/_code.scss index 4222c652193..a97f04bc84b 100644 --- a/public/sass/base/_code.scss +++ b/public/sass/base/_code.scss @@ -11,15 +11,12 @@ pre { background-color: $code-tag-bg; color: $text-color; border: 1px solid $code-tag-border; - padding: 10px; border-radius: 4px; } // Inline code code { color: $text-color; - background-color: $code-tag-bg; - border: 1px solid darken($code-tag-bg, 15%); white-space: nowrap; padding: 2px 5px; margin: 0 2px; @@ -27,7 +24,7 @@ code { code.code--small { font-size: $font-size-xs; - padding: 5px; + padding: 0.2rem; margin: 0 2px; } @@ -41,6 +38,7 @@ pre { white-space: pre; white-space: pre-wrap; background-color: $code-tag-bg; + padding: 10px; // Make prettyprint styles more spaced out for readability &.prettyprint { diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index 110cb548467..3f4cd96c444 100644 --- a/public/test/specs/templateValuesSrv-specs.js +++ b/public/test/specs/templateValuesSrv-specs.js @@ -92,6 +92,7 @@ define([ var ds = {}; ds.metricFindQuery = sinon.stub().returns(ctx.$q.when(scenario.queryResult)); ctx.datasourceSrv.get = sinon.stub().returns(ctx.$q.when(ds)); + ctx.datasourceSrv.getMetricSources = sinon.stub().returns(scenario.metricSources); ctx.service.updateOptions(scenario.variable); ctx.$rootScope.$digest(); @@ -137,7 +138,6 @@ define([ }); }); - describeUpdateVariable('interval variable with auto', function(scenario) { scenario.setup(function() { scenario.variable = { type: 'interval', query: '1s,2h,5h,1d', name: 'test', auto: true, auto_count: 10 }; @@ -237,7 +237,7 @@ define([ }); }); - describeUpdateVariable('regex pattern without slashes', function(scenario) { + describeUpdateVariable('regex pattern without slashes', function(scenario) { scenario.setup(function() { scenario.variable = { type: 'query', query: 'apps.*', name: 'test' }; scenario.variable.regex = 'backend_01'; @@ -284,6 +284,23 @@ define([ }); }); - }); + describeUpdateVariable('datasource variable with regex filter', function(scenario) { + scenario.setup(function() { + scenario.variable = {type: 'datasource', query: 'graphite', name: 'test', current: {}, regex: '/pee$/' }; + scenario.metricSources = [ + {name: 'backend1', meta: {id: 'influx'}}, + {name: 'backend2_pee', meta: {id: 'graphite'}}, + {name: 'backend3', meta: {id: 'graphite'}}, + {name: 'backend4_pee', meta: {id: 'graphite'}}, + ]; + }); + it('should set only contain graphite ds and filtered using regex', function() { + expect(scenario.variable.options.length).to.be(2); + expect(scenario.variable.options[0].value).to.be('backend2_pee'); + expect(scenario.variable.options[1].value).to.be('backend4_pee'); + }); + }); + + }); });