ソースを参照

Merge branch 'master' into alerting

Torkel Ödegaard 9 年 前
コミット
4fd8b2ace4

+ 2 - 1
CHANGELOG.md

@@ -7,7 +7,7 @@
 * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021)
 * **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)
 * **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)
 * **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)
 * **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)
 * **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)
 * **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)
 * **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)
 * **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)
 # 3.1.0 stable (2016-07-12)
 
 

+ 3 - 5
circle.yml

@@ -18,15 +18,13 @@ dependencies:
 
 
 test:
 test:
   override:
   override:
-     # FMT
      - test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
      - test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
-     # GO VET
      - go vet ./pkg/...
      - go vet ./pkg/...
-     # Go test
-     - godep go test -v ./pkg/...
-     # js tests
+     # JS tests
      - npm test
      - npm test
      - npm run coveralls
      - npm run coveralls
+     # GO tests
+     - godep go test -v ./pkg/...
 
 
 deployment:
 deployment:
   master:
   master:

+ 1 - 1
conf/defaults.ini

@@ -378,7 +378,7 @@ interval_seconds  = 60
 # Send internal Grafana metrics to graphite
 # Send internal Grafana metrics to graphite
 ; [metrics.graphite]
 ; [metrics.graphite]
 ; address = localhost:2003
 ; address = localhost:2003
-; prefix = prod.grafana.%(instance_name)s.
+; prefix = service.grafana.%(instance_name)s
 
 
 [grafana_net]
 [grafana_net]
 url = https://grafana.net
 url = https://grafana.net

+ 2 - 2
conf/sample.ini

@@ -298,7 +298,7 @@ check_for_updates = true
 # Metrics available at HTTP API Url /api/metrics
 # Metrics available at HTTP API Url /api/metrics
 [metrics]
 [metrics]
 # Disable / Enable internal metrics
 # Disable / Enable internal metrics
-;enabled           = true
+enabled           = true
 
 
 # Publish interval
 # Publish interval
 ;interval_seconds  = 10
 ;interval_seconds  = 10
@@ -306,7 +306,7 @@ check_for_updates = true
 # Send internal metrics to Graphite
 # Send internal metrics to Graphite
 ; [metrics.graphite]
 ; [metrics.graphite]
 ; address = localhost:2003
 ; address = localhost:2003
-; prefix = prod.grafana.%(instance_name)s.
+; prefix = service.grafana.%(instance_name)s
 
 
 #################################### Internal Grafana Metrics ##########################
 #################################### Internal Grafana Metrics ##########################
 # Url used to to import dashboards directly from Grafana.net
 # Url used to to import dashboards directly from Grafana.net

+ 1 - 1
docs/sources/guides/gettingstarted.md

@@ -5,7 +5,7 @@ page_keywords: grafana, guide, documentation
 ---
 ---
 
 
 # Getting started
 # 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
 ## 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.
 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.

+ 12 - 1
docs/sources/installation/configuration.md

@@ -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
 The port to bind to, defaults to `3000`. To use port 80 you need to
 either give the Grafana binary permission for example:
 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:
 Or redirect port 80 to the Grafana port using:
 
 
@@ -477,3 +477,14 @@ Format `<Hostname or ip>`:port
 ### prefix
 ### prefix
 Graphite metric prefix. Defaults to `prod.grafana.%(instance_name)s.`
 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`
+

+ 3 - 3
docs/sources/installation/debian.md

@@ -10,13 +10,13 @@ page_keywords: grafana, installation, debian, ubuntu, guide
 
 
 Description | Download
 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
 ## 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 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
 ## APT Repository
 
 

+ 4 - 15
docs/sources/installation/mac.md

@@ -11,28 +11,17 @@ Installation can be done using [homebrew](http://brew.sh/)
 Install latest stable:
 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
 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
 To upgrade use the reinstall command
 
 
 ```
 ```
-brew reinstall --HEAD grafana/grafana/grafana
+brew update
+brew reinstall grafana
 ```
 ```
 
 
 
 

+ 4 - 4
docs/sources/installation/rpm.md

@@ -10,24 +10,24 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
 
 
 Description | Download
 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
 ## Install Latest Stable
 
 
 You can install Grafana using Yum directly.
 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`.
 Or install manually using `rpm`.
 
 
 #### On CentOS / Fedora / Redhat:
 #### On CentOS / Fedora / Redhat:
 
 
     $ sudo yum install initscripts fontconfig
     $ 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:
 #### 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
 ## Install via YUM Repository
 
 

+ 1 - 1
docs/sources/installation/windows.md

@@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide
 
 
 Description | Download
 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
 ## Configure
 
 

+ 1 - 1
docs/sources/reference/playlist.md

@@ -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.
 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.
 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.
 
 

+ 2 - 2
latest.json

@@ -1,4 +1,4 @@
 {
 {
-  "stable": "3.1.0",
-	"testing": "3.1.0"
+  "stable": "3.1.1",
+	"testing": "3.1.1"
 }
 }

+ 8 - 8
packaging/publish/publish.sh

@@ -1,20 +1,20 @@
 #! /usr/bin/env bash
 #! /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/jessie grafana_${deb_ver}_amd64.deb
 package_cloud push grafana/testing/debian/wheezy 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/6 grafana-${rpm_ver}.x86_64.rpm
 package_cloud push grafana/testing/el/7 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

+ 11 - 2
pkg/metrics/graphite.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"bytes"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
@@ -20,14 +21,22 @@ type GraphitePublisher struct {
 func CreateGraphitePublisher() (*GraphitePublisher, error) {
 func CreateGraphitePublisher() (*GraphitePublisher, error) {
 	graphiteSection, err := setting.Cfg.GetSection("metrics.graphite")
 	graphiteSection, err := setting.Cfg.GetSection("metrics.graphite")
 	if err != nil {
 	if err != nil {
-		return nil, nil
+		return nil, err
 	}
 	}
 
 
 	publisher := &GraphitePublisher{}
 	publisher := &GraphitePublisher{}
 	publisher.prevCounts = make(map[string]int64)
 	publisher.prevCounts = make(map[string]int64)
 	publisher.protocol = "tcp"
 	publisher.protocol = "tcp"
 	publisher.address = graphiteSection.Key("address").MustString("localhost:2003")
 	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
 	return publisher, nil
 }
 }

+ 55 - 0
pkg/metrics/graphite_test.go

@@ -0,0 +1,55 @@
+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")
+	})
+}

+ 0 - 182
public/app/features/dashboard/dynamicDashboardSrv.js

@@ -1,182 +0,0 @@
-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;
-      });
-    };
-
-  });
-});

+ 2 - 6
public/app/features/dashboard/dynamic_dashboard_srv.ts

@@ -10,10 +10,6 @@ export class DynamicDashboardSrv {
   iteration: number;
   iteration: number;
   dashboard: any;
   dashboard: any;
 
 
-  constructor() {
-    this.iteration = new Date().getTime();
-  }
-
   init(dashboard) {
   init(dashboard) {
     if (dashboard.snapshot) { return; }
     if (dashboard.snapshot) { return; }
     this.process(dashboard, {});
     this.process(dashboard, {});
@@ -21,14 +17,14 @@ export class DynamicDashboardSrv {
 
 
   update(dashboard) {
   update(dashboard) {
     if (dashboard.snapshot) { return; }
     if (dashboard.snapshot) { return; }
-
-    this.iteration = this.iteration + 1;
     this.process(dashboard, {});
     this.process(dashboard, {});
   }
   }
 
 
   process(dashboard, options) {
   process(dashboard, options) {
     if (dashboard.templating.list.length === 0) { return; }
     if (dashboard.templating.list.length === 0) { return; }
+
     this.dashboard = dashboard;
     this.dashboard = dashboard;
+    this.iteration = (this.iteration || new Date().getTime()) + 1;
 
 
     var cleanUpOnly = options.cleanUpOnly;
     var cleanUpOnly = options.cleanUpOnly;
 
 

+ 6 - 2
public/app/features/dashboard/timeSrv.js

@@ -20,12 +20,13 @@ define([
 
 
       this.dashboard = dashboard;
       this.dashboard = dashboard;
       this.time = dashboard.time;
       this.time = dashboard.time;
+      this.refresh = dashboard.refresh;
 
 
       this._initTimeFromUrl();
       this._initTimeFromUrl();
       this._parseTime();
       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) {
       if ($routeParams.to) {
         this.time.to = this._parseUrlParam($routeParams.to) || this.time.to;
         this.time.to = this._parseUrlParam($routeParams.to) || this.time.to;
       }
       }
+      if ($routeParams.refresh) {
+        this.refresh = $routeParams.refresh || this.refresh;
+      }
     };
     };
 
 
     this.setAutoRefresh = function (interval) {
     this.setAutoRefresh = function (interval) {

+ 3 - 1
public/app/features/templating/templateValuesSrv.js

@@ -166,7 +166,9 @@ function (angular, _, $, kbn) {
         if (otherVariable === updatedVariable) {
         if (otherVariable === updatedVariable) {
           return;
           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)) {
             templateSrv.containsVariable(otherVariable.datasource, updatedVariable.name)) {
           return self.updateOptions(otherVariable);
           return self.updateOptions(otherVariable);
         }
         }

+ 2 - 2
public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts

@@ -59,8 +59,8 @@ describe('ElasticDatasource', function() {
 
 
       ctx.ds.query({
       ctx.ds.query({
         range: {
         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' }]
         targets: [{ bucketAggs: [], metrics: [], query: 'escape\\:test' }]
       });
       });

+ 19 - 7
public/app/plugins/panel/graph/graph_tooltip.js

@@ -1,7 +1,8 @@
 define([
 define([
   'jquery',
   'jquery',
+  'lodash'
 ],
 ],
-function ($) {
+function ($, _) {
   'use strict';
   'use strict';
 
 
   function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
   function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
@@ -40,7 +41,7 @@ function ($) {
     };
     };
 
 
     this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) {
     this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) {
-      var value, i, series, hoverIndex;
+      var value, i, series, hoverIndex, hoverDistance, pointTime;
       var results = [];
       var results = [];
 
 
       //now we know the current X (j) position for X and Y values
       //now we know the current X (j) position for X and Y values
@@ -60,7 +61,8 @@ function ($) {
         }
         }
 
 
         hoverIndex = this.findHoverIndexFromData(pos.x, series);
         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 (series.stack) {
           if (panel.tooltip.value_type === 'individual') {
           if (panel.tooltip.value_type === 'individual') {
@@ -80,13 +82,23 @@ function ($) {
           // stacked and steppedLine plots can have series with different length.
           // 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,
           // 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.
           // 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;
       return results;
     };
     };
 
 

+ 2 - 2
public/sass/_variables.light.scss

@@ -60,8 +60,8 @@ $page-bg:  		  		    $white;
 $body-color:    		    $gray-1;
 $body-color:    		    $gray-1;
 $text-color:    		    $gray-1;
 $text-color:    		    $gray-1;
 $text-color-strong:	    $white;
 $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-color-emphasis: 	$dark-5;
 
 
 $text-shadow-strong: none;
 $text-shadow-strong: none;

+ 1 - 1
public/sass/components/_switch.scss

@@ -64,7 +64,7 @@ $switch-height: 1.5rem;
   input + label::before {
   input + label::before {
     font-family: 'FontAwesome';
     font-family: 'FontAwesome';
     content: "\f096"; // square-o
     content: "\f096"; // square-o
-    color: $text-color-faint;
+    color: $text-color-weak;
     transition: transform 0.4s;
     transition: transform 0.4s;
     backface-visibility: hidden;
     backface-visibility: hidden;
     text-shadow: $text-shadow-faint;
     text-shadow: $text-shadow-faint;

+ 0 - 2
public/sass/pages/_dashboard.scss

@@ -113,8 +113,6 @@ div.flot-text {
 .panel {
 .panel {
   display: inline-block;
   display: inline-block;
   float: left;
   float: left;
-  vertical-align: top;
-  position: relative;
 }
 }
 
 
 .panel-margin {
 .panel-margin {

+ 2 - 1
public/test/specs/dynamicDashboardSrv-specs.js

@@ -1,5 +1,5 @@
 define([
 define([
-  'app/features/dashboard/dynamicDashboardSrv',
+  'app/features/dashboard/dynamic_dashboard_srv',
   'app/features/dashboard/dashboardSrv'
   'app/features/dashboard/dashboardSrv'
 ], function() {
 ], function() {
   'use strict';
   'use strict';
@@ -12,6 +12,7 @@ define([
       ctx.setup = function (setupFunc) {
       ctx.setup = function (setupFunc) {
 
 
         beforeEach(module('grafana.services'));
         beforeEach(module('grafana.services'));
+        beforeEach(module('grafana.core'));
         beforeEach(module(function($provide) {
         beforeEach(module(function($provide) {
           $provide.value('contextSrv', {
           $provide.value('contextSrv', {
             user: { timezone: 'utc'}
             user: { timezone: 'utc'}

+ 1 - 1
public/views/index.html

@@ -55,7 +55,7 @@
 							</a>
 							</a>
 						</li>
 						</li>
 						<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>
 								<i class="fa fa-comments-o"></i>
 								Community
 								Community
 							</a>
 							</a>