Commit 4fd8b2ac by Torkel Ödegaard

Merge branch 'master' into alerting

parents 61756d2a a56cf9d6
......@@ -7,7 +7,7 @@
* **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021)
* **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689)
# 3.1.1 (unreleased / v3.1.x branch)
# 3.1.1 (2016-08-01)
* **IFrame embedding**: Fixed issue of using full iframe height, fixes [#5605](https://github.com/grafana/grafana/issues/5606)
* **Panel PNG rendering**: Fixed issue detecting render completion, fixes [#5605](https://github.com/grafana/grafana/issues/5606)
* **Elasticsearch**: Fixed issue with templating query and json parse error, fixes [#5615](https://github.com/grafana/grafana/issues/5615)
......@@ -15,6 +15,7 @@
* **Graphite**: Fixed issue with mixed data sources and Graphite, fixes [#5617](https://github.com/grafana/grafana/issues/5617)
* **Templating**: Fixed issue with template variable query was issued multiple times during dashboard load, fixes [#5637](https://github.com/grafana/grafana/issues/5637)
* **Zoom**: Fixed issues with zoom in and out on embedded (iframed) panel, fixes [#4489](https://github.com/grafana/grafana/issues/4489), [#5666](https://github.com/grafana/grafana/issues/5666)
* **Templating**: Row/Panel repeat issue when saving dashboard caused dupes to appear, fixes [#5591](https://github.com/grafana/grafana/issues/5591)
# 3.1.0 stable (2016-07-12)
......
......@@ -18,15 +18,13 @@ dependencies:
test:
override:
# FMT
- test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
# GO VET
- go vet ./pkg/...
# Go test
- godep go test -v ./pkg/...
# js tests
# JS tests
- npm test
- npm run coveralls
# GO tests
- godep go test -v ./pkg/...
deployment:
master:
......
......@@ -378,7 +378,7 @@ interval_seconds = 60
# Send internal Grafana metrics to graphite
; [metrics.graphite]
; address = localhost:2003
; prefix = prod.grafana.%(instance_name)s.
; prefix = service.grafana.%(instance_name)s
[grafana_net]
url = https://grafana.net
......
......@@ -298,7 +298,7 @@ check_for_updates = true
# Metrics available at HTTP API Url /api/metrics
[metrics]
# Disable / Enable internal metrics
;enabled = true
enabled = true
# Publish interval
;interval_seconds = 10
......@@ -306,7 +306,7 @@ check_for_updates = true
# Send internal metrics to Graphite
; [metrics.graphite]
; address = localhost:2003
; prefix = prod.grafana.%(instance_name)s.
; prefix = service.grafana.%(instance_name)s
#################################### Internal Grafana Metrics ##########################
# Url used to to import dashboards directly from Grafana.net
......
......@@ -5,7 +5,7 @@ page_keywords: grafana, guide, documentation
---
# Getting started
This guide will help you get started and acquainted with Grafana. It assumes you have a working Grafana 2.x instance, and have added at least one [Data Source](/datasources/overview).
This guide will help you get started and acquainted with Grafana. It assumes you have a working Grafana server up and running and have added at least one [Data Source](/datasources/overview).
## Beginner guides
Watch the 10min [beginners guide to building dashboards](https://www.youtube.com/watch?v=sKNZMtoSHN4&index=7&list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2) to get a quick intro to setting up Dashboards and Panels.
......
......@@ -76,7 +76,7 @@ The IP address to bind to. If empty will bind to all interfaces
The port to bind to, defaults to `3000`. To use port 80 you need to
either give the Grafana binary permission for example:
$ sudo setcap 'cap_net_bind_service=+ep' /opt/grafana/current/grafana
$ sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/grafana-server
Or redirect port 80 to the Grafana port using:
......@@ -477,3 +477,14 @@ Format `<Hostname or ip>`:port
### prefix
Graphite metric prefix. Defaults to `prod.grafana.%(instance_name)s.`
## [snapshots]
### external_enabled
Set to false to disable external snapshot publish endpoint (default true)
### external_snapshot_url
Set root url to a Grafana instance where you want to publish external snapshots (defaults to https://snapshots-origin.raintank.io)
### external_snapshot_name
Set name for external snapshot button. Defaults to `Publish to snapshot.raintank.io`
......@@ -10,13 +10,13 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download
------------ | -------------
Stable .deb for Debian-based Linux | [3.1.0](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1468321182_amd64.deb)
Stable .deb for Debian-based Linux | [3.1.1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb)
## Install Stable
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1468321182_amd64.deb
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.1.0-1468321182_amd64.deb
$ sudo dpkg -i grafana_3.1.1-1470047149_amd64.deb
## APT Repository
......
......@@ -11,28 +11,17 @@ Installation can be done using [homebrew](http://brew.sh/)
Install latest stable:
```
brew install grafana/grafana/grafana
```
To start grafana look at the command printed after the homebrew install completes.
You can also add the grafana as tap.
```
brew tap grafana/grafana
brew update
brew install grafana
```
Install latest unstable from master:
```
brew install --HEAD grafana/grafana/grafana
```
To start grafana look at the command printed after the homebrew install completes.
To upgrade use the reinstall command
```
brew reinstall --HEAD grafana/grafana/grafana
brew update
brew reinstall grafana
```
......@@ -10,24 +10,24 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download
------------ | -------------
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.0 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1468321182.x86_64.rpm)
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm)
## Install Latest Stable
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1468321182.x86_64.rpm
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-3.1.0-1468321182.x86_64.rpm
$ sudo rpm -Uvh grafana-3.1.1-1470047149.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-3.1.0-1468321182.x86_64.rpm
$ sudo rpm -i --nodeps grafana-3.1.1-1470047149.x86_64.rpm
## Install via YUM Repository
......
......@@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide
Description | Download
------------ | -------------
Stable Zip package for Windows | [grafana.3.1.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.0.windows-x64.zip)
Stable Zip package for Windows | [grafana.3.1.1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.1.windows-x64.zip)
## Configure
......
......@@ -14,7 +14,7 @@ Since Grafana automatically scales Dashboards to any resolution they're perfect
The Playlist feature can be accessed from Grafana's sidemenu. Click the 'Playlist' button from the sidemenu to access the Playlist functionality. When 'Playlist' button is clicked, playlist view will open up showing saved playlists and an option to create new playlists.
<img src="/img/v2/dashboard_search.png" class="no-shadow">
<img src="/img/v3/playlist.png" class="no-shadow">
Click on "New Playlist" button to create a new playlist. Firstly, name your playlist and configure a time interval for Grafana to wait on a particular Dashboard before advancing to the next one on the Playlist.
......
{
"stable": "3.1.0",
"testing": "3.1.0"
"stable": "3.1.1",
"testing": "3.1.1"
}
#! /usr/bin/env bash
deb_ver=3.1.0-1466666977beta1
rpm_ver=3.1.0-1466666977beta1
deb_ver=3.1.1-1470047149
rpm_ver=3.1.1-1470047149
# wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
# wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm
......@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"net"
"strings"
"time"
"github.com/grafana/grafana/pkg/log"
......@@ -20,14 +21,22 @@ type GraphitePublisher struct {
func CreateGraphitePublisher() (*GraphitePublisher, error) {
graphiteSection, err := setting.Cfg.GetSection("metrics.graphite")
if err != nil {
return nil, nil
return nil, err
}
publisher := &GraphitePublisher{}
publisher.prevCounts = make(map[string]int64)
publisher.protocol = "tcp"
publisher.address = graphiteSection.Key("address").MustString("localhost:2003")
publisher.prefix = graphiteSection.Key("prefix").MustString("service.grafana.%(instance_name)s")
safeInstanceName := strings.Replace(setting.InstanceName, ".", "_", -1)
prefix := graphiteSection.Key("prefix").Value()
if prefix == "" {
prefix = "service.grafana.%(instance_name)s"
}
publisher.prefix = strings.Replace(prefix, "%(instance_name)s", safeInstanceName, -1)
return publisher, nil
}
......
package metrics
import (
"testing"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
)
func TestGraphitePublisher(t *testing.T) {
Convey("Test graphite prefix replacement", t, func() {
var err error
err = setting.NewConfigContext(&setting.CommandLineArgs{
HomePath: "../../",
})
So(err, ShouldBeNil)
sec, err := setting.Cfg.NewSection("metrics.graphite")
sec.NewKey("prefix", "service.grafana.%(instance_name)s")
sec.NewKey("address", "localhost:2003")
So(err, ShouldBeNil)
setting.InstanceName = "hostname.with.dots.com"
publisher, err := CreateGraphitePublisher()
So(err, ShouldBeNil)
So(publisher, ShouldNotBeNil)
So(publisher.prefix, ShouldEqual, "service.grafana.hostname_with_dots_com")
})
Convey("Test graphite publisher default values", t, func() {
var err error
err = setting.NewConfigContext(&setting.CommandLineArgs{
HomePath: "../../",
})
So(err, ShouldBeNil)
_, err = setting.Cfg.NewSection("metrics.graphite")
setting.InstanceName = "hostname.with.dots.com"
publisher, err := CreateGraphitePublisher()
So(err, ShouldBeNil)
So(publisher, ShouldNotBeNil)
So(publisher.prefix, ShouldEqual, "service.grafana.hostname_with_dots_com")
So(publisher.address, ShouldEqual, "localhost:2003")
})
}
define([
'angular',
'lodash',
],
function (angular, _) {
'use strict';
var module = angular.module('grafana.services');
module.service('dynamicDashboardSrv', function() {
var self = this;
this.init = function(dashboard) {
if (dashboard.snapshot) { return; }
this.iteration = new Date().getTime();
this.process(dashboard);
};
this.update = function(dashboard) {
if (dashboard.snapshot) { return; }
this.iteration = this.iteration + 1;
this.process(dashboard);
};
this.process = function(dashboard) {
if (dashboard.templating.list.length === 0) { return; }
this.dashboard = dashboard;
var i, j, row, panel;
for (i = 0; i < this.dashboard.rows.length; i++) {
row = this.dashboard.rows[i];
// handle row repeats
if (row.repeat) {
this.repeatRow(row, i);
}
// clean up old left overs
else if (row.repeatRowId && row.repeatIteration !== this.iteration) {
this.dashboard.rows.splice(i, 1);
i = i - 1;
continue;
}
// repeat panels
for (j = 0; j < row.panels.length; j++) {
panel = row.panels[j];
if (panel.repeat) {
this.repeatPanel(panel, row);
}
// clean up old left overs
else if (panel.repeatPanelId && panel.repeatIteration !== this.iteration) {
row.panels = _.without(row.panels, panel);
j = j - 1;
} else if (row.repeat || row.repeatRowId) {
continue;
} else if (!_.isEmpty(panel.scopedVars) && panel.repeatIteration !== this.iteration) {
panel.scopedVars = {};
}
}
}
};
// returns a new row clone or reuses a clone from previous iteration
this.getRowClone = function(sourceRow, repeatIndex, sourceRowIndex) {
if (repeatIndex === 0) {
return sourceRow;
}
var i, panel, row, copy;
var sourceRowId = sourceRowIndex + 1;
// look for row to reuse
for (i = 0; i < this.dashboard.rows.length; i++) {
row = this.dashboard.rows[i];
if (row.repeatRowId === sourceRowId && row.repeatIteration !== this.iteration) {
copy = row;
break;
}
}
if (!copy) {
copy = angular.copy(sourceRow);
this.dashboard.rows.splice(sourceRowIndex + repeatIndex, 0, copy);
// set new panel ids
for (i = 0; i < copy.panels.length; i++) {
panel = copy.panels[i];
panel.id = this.dashboard.getNextPanelId();
}
}
copy.repeat = null;
copy.repeatRowId = sourceRowId;
copy.repeatIteration = this.iteration;
return copy;
};
// returns a new row clone or reuses a clone from previous iteration
this.repeatRow = function(row, rowIndex) {
var variables = this.dashboard.templating.list;
var variable = _.findWhere(variables, {name: row.repeat});
if (!variable) {
return;
}
var selected, copy, i, panel;
if (variable.current.text === 'All') {
selected = variable.options.slice(1, variable.options.length);
} else {
selected = _.filter(variable.options, {selected: true});
}
_.each(selected, function(option, index) {
copy = self.getRowClone(row, index, rowIndex);
copy.scopedVars = {};
copy.scopedVars[variable.name] = option;
for (i = 0; i < copy.panels.length; i++) {
panel = copy.panels[i];
panel.scopedVars = {};
panel.scopedVars[variable.name] = option;
}
}, this);
};
this.getPanelClone = function(sourcePanel, row, index) {
// if first clone return source
if (index === 0) {
return sourcePanel;
}
var i, tmpId, panel, clone;
// first try finding an existing clone to use
for (i = 0; i < row.panels.length; i++) {
panel = row.panels[i];
if (panel.repeatIteration !== this.iteration && panel.repeatPanelId === sourcePanel.id) {
clone = panel;
break;
}
}
if (!clone) {
clone = { id: this.dashboard.getNextPanelId() };
row.panels.push(clone);
}
// save id
tmpId = clone.id;
// copy properties from source
angular.copy(sourcePanel, clone);
// restore id
clone.id = tmpId;
clone.repeatIteration = this.iteration;
clone.repeatPanelId = sourcePanel.id;
clone.repeat = null;
return clone;
};
this.repeatPanel = function(panel, row) {
var variables = this.dashboard.templating.list;
var variable = _.findWhere(variables, {name: panel.repeat});
if (!variable) { return; }
var selected;
if (variable.current.text === 'All') {
selected = variable.options.slice(1, variable.options.length);
} else {
selected = _.filter(variable.options, {selected: true});
}
_.each(selected, function(option, index) {
var copy = self.getPanelClone(panel, row, index);
copy.span = Math.max(12 / selected.length, panel.minSpan);
copy.scopedVars = copy.scopedVars || {};
copy.scopedVars[variable.name] = option;
});
};
});
});
......@@ -10,10 +10,6 @@ export class DynamicDashboardSrv {
iteration: number;
dashboard: any;
constructor() {
this.iteration = new Date().getTime();
}
init(dashboard) {
if (dashboard.snapshot) { return; }
this.process(dashboard, {});
......@@ -21,14 +17,14 @@ export class DynamicDashboardSrv {
update(dashboard) {
if (dashboard.snapshot) { return; }
this.iteration = this.iteration + 1;
this.process(dashboard, {});
}
process(dashboard, options) {
if (dashboard.templating.list.length === 0) { return; }
this.dashboard = dashboard;
this.iteration = (this.iteration || new Date().getTime()) + 1;
var cleanUpOnly = options.cleanUpOnly;
......
......@@ -20,12 +20,13 @@ define([
this.dashboard = dashboard;
this.time = dashboard.time;
this.refresh = dashboard.refresh;
this._initTimeFromUrl();
this._parseTime();
if(this.dashboard.refresh) {
this.setAutoRefresh(this.dashboard.refresh);
if(this.refresh) {
this.setAutoRefresh(this.refresh);
}
};
......@@ -65,6 +66,9 @@ define([
if ($routeParams.to) {
this.time.to = this._parseUrlParam($routeParams.to) || this.time.to;
}
if ($routeParams.refresh) {
this.refresh = $routeParams.refresh || this.refresh;
}
};
this.setAutoRefresh = function (interval) {
......
......@@ -166,7 +166,9 @@ function (angular, _, $, kbn) {
if (otherVariable === updatedVariable) {
return;
}
if (templateSrv.containsVariable(otherVariable.query, updatedVariable.name) ||
if ((otherVariable.type === "datasource" &&
templateSrv.containsVariable(otherVariable.regex, updatedVariable.name)) ||
templateSrv.containsVariable(otherVariable.query, updatedVariable.name) ||
templateSrv.containsVariable(otherVariable.datasource, updatedVariable.name)) {
return self.updateOptions(otherVariable);
}
......
......@@ -59,8 +59,8 @@ describe('ElasticDatasource', function() {
ctx.ds.query({
range: {
from: moment([2015, 4, 30, 10]),
to: moment([2015, 5, 1, 10])
from: moment.utc([2015, 4, 30, 10]),
to: moment.utc([2015, 5, 1, 10])
},
targets: [{ bucketAggs: [], metrics: [], query: 'escape\\:test' }]
});
......
define([
'jquery',
'lodash'
],
function ($) {
function ($, _) {
'use strict';
function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
......@@ -40,7 +41,7 @@ function ($) {
};
this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) {
var value, i, series, hoverIndex;
var value, i, series, hoverIndex, hoverDistance, pointTime;
var results = [];
//now we know the current X (j) position for X and Y values
......@@ -60,7 +61,8 @@ function ($) {
}
hoverIndex = this.findHoverIndexFromData(pos.x, series);
results.time = series.data[hoverIndex][0];
hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]);
pointTime = series.data[hoverIndex][0];
if (series.stack) {
if (panel.tooltip.value_type === 'individual') {
......@@ -80,13 +82,23 @@ function ($) {
// stacked and steppedLine plots can have series with different length.
// Stacked series can increase its length on each new stacked serie if null points found,
// to speed the index search we begin always on the last found hoverIndex.
var newhoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex);
results.push({ value: value, hoverIndex: newhoverIndex, color: series.color, label: series.label });
} else {
results.push({ value: value, hoverIndex: hoverIndex, color: series.color, label: series.label });
hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex);
hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]);
}
results.push({
value: value,
hoverIndex: hoverIndex,
color: series.color,
label: series.label,
time: pointTime,
distance: hoverDistance
});
}
// Find point which closer to pointer
results.time = _.min(results, 'distance').time;
return results;
};
......
......@@ -60,8 +60,8 @@ $page-bg: $white;
$body-color: $gray-1;
$text-color: $gray-1;
$text-color-strong: $white;
$text-color-weak: $gray-2;
$text-color-faint: $gray-3;
$text-color-weak: $gray-3;
$text-color-faint: $gray-4;
$text-color-emphasis: $dark-5;
$text-shadow-strong: none;
......
......@@ -64,7 +64,7 @@ $switch-height: 1.5rem;
input + label::before {
font-family: 'FontAwesome';
content: "\f096"; // square-o
color: $text-color-faint;
color: $text-color-weak;
transition: transform 0.4s;
backface-visibility: hidden;
text-shadow: $text-shadow-faint;
......
......@@ -113,8 +113,6 @@ div.flot-text {
.panel {
display: inline-block;
float: left;
vertical-align: top;
position: relative;
}
.panel-margin {
......
define([
'app/features/dashboard/dynamicDashboardSrv',
'app/features/dashboard/dynamic_dashboard_srv',
'app/features/dashboard/dashboardSrv'
], function() {
'use strict';
......@@ -12,6 +12,7 @@ define([
ctx.setup = function (setupFunc) {
beforeEach(module('grafana.services'));
beforeEach(module('grafana.core'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', {
user: { timezone: 'utc'}
......
......@@ -55,7 +55,7 @@
</a>
</li>
<li>
<a href="https://grafana.org/community" target="_blank">
<a href="http://grafana.org/community" target="_blank">
<i class="fa fa-comments-o"></i>
Community
</a>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment