From d018d2a23f50921ba39e84082da7500f0e55d959 Mon Sep 17 00:00:00 2001
From: Mitsuhiro Tanda
Date: Wed, 28 Sep 2016 21:31:26 +0900
Subject: [PATCH 001/365] (prometheus) table support
---
.../datasource/prometheus/datasource.ts | 54 +++++++++++++++++--
.../prometheus/partials/query.editor.html | 4 ++
.../datasource/prometheus/query_ctrl.ts | 6 +++
.../prometheus/specs/datasource_specs.ts | 24 +++++++++
4 files changed, 85 insertions(+), 3 deletions(-)
diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts
index 972fa2c7c55..4ba24a99fb1 100644
--- a/public/app/plugins/datasource/prometheus/datasource.ts
+++ b/public/app/plugins/datasource/prometheus/datasource.ts
@@ -6,6 +6,7 @@ import moment from 'moment';
import * as dateMath from 'app/core/utils/datemath';
import PrometheusMetricFindQuery from './metric_find_query';
+import TableModel from 'app/core/table_model';
var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
@@ -117,9 +118,18 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
throw response.error;
}
delete self.lastErrors.query;
- _.each(response.data.data.result, function(metricData) {
- result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
- });
+ switch (activeTargets[index].resultFormat) {
+ case 'table': {
+ result.push(self.transformMetricDataToTable(response.data.data.result));
+ break;
+ }
+ default: {
+ _.each(response.data.data.result, function(metricData) {
+ result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
+ });
+ break;
+ }
+ }
});
return { data: result };
@@ -260,6 +270,44 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
return { target: metricLabel, datapoints: dps };
};
+ this.transformMetricDataToTable = function(series) {
+ var table = new TableModel();
+ var self = this;
+ var i, j;
+
+ if (series.length === 0) {
+ return table;
+ }
+
+ _.each(series, function(series, seriesIndex) {
+ if (seriesIndex === 0) {
+ table.columns.push({text: 'Time', type: 'time'});
+ _.each(_.keys(series.metric), function(key) {
+ table.columns.push({text: key});
+ });
+ table.columns.push({text: 'Value'});
+ }
+
+ if (series.values) {
+ for (i = 0; i < series.values.length; i++) {
+ var values = series.values[i];
+ var reordered = [values[0] * 1000];
+ if (series.metric) {
+ for (var key in series.metric) {
+ if (series.metric.hasOwnProperty(key)) {
+ reordered.push(series.metric[key]);
+ }
+ }
+ }
+ reordered.push(parseFloat(values[1]));
+ table.rows.push(reordered);
+ }
+ }
+ });
+
+ return table;
+ };
+
this.createMetricLabel = function(labelData, options) {
if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
return this.getOriginalMetricName(labelData);
diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html
index 37c27ace58a..c4db774565f 100644
--- a/public/app/plugins/datasource/prometheus/partials/query.editor.html
+++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html
@@ -39,6 +39,10 @@
ng-change="ctrl.refreshMetricData()">
+
+
+
+
- Title
-
+ Title
+
- Time
-
+ Time
+ Time Start
+
+
+
+ Time Stop
+
-
- Description
-
+ Description
+ Description Start
+
+
+
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts
index 3a75c189dbd..5aaf5324156 100755
--- a/public/app/plugins/panel/graph/graph.ts
+++ b/public/app/plugins/panel/graph/graph.ts
@@ -83,7 +83,13 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv) {
// Select time for new annotation
if (ctrl.inAddAnnotationMode) {
- ctrl.showAddAnnotationModal(event);
+ let timeRange = {
+ from: event.pos.x,
+ to: null
+ };
+
+ ctrl.showAddAnnotationModal(timeRange);
+ ctrl.inAddAnnotationMode = false;
}
}, scope);
@@ -647,12 +653,20 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv) {
}
elem.bind("plotselected", function (event, ranges) {
- scope.$apply(function() {
- timeSrv.setTime({
- from : moment.utc(ranges.xaxis.from),
- to : moment.utc(ranges.xaxis.to),
+ if (ctrl.inAddAnnotationMode) {
+ // Select time range for new annotation
+ let timeRange = ranges.xaxis;
+ ctrl.showAddAnnotationModal(timeRange);
+ plot.clearSelection();
+ ctrl.inAddAnnotationMode = false;
+ } else {
+ scope.$apply(function() {
+ timeSrv.setTime({
+ from : moment.utc(ranges.xaxis.from),
+ to : moment.utc(ranges.xaxis.to),
+ });
});
- });
+ }
});
scope.$on('$destroy', function() {
diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts
index b8ab763550b..5a24f39cf80 100644
--- a/public/app/plugins/panel/graph/module.ts
+++ b/public/app/plugins/panel/graph/module.ts
@@ -308,14 +308,13 @@ class GraphCtrl extends MetricsPanelCtrl {
}
// Get annotation info from dialog and push it to backend
- pushAnnotation(annotation) {
- return this.annotationsSrv.postAnnotation(annotation);
+ pushAnnotations(annotations) {
+ return this.annotationsSrv.postAnnotation(annotations);
}
- showAddAnnotationModal(event) {
+ showAddAnnotationModal(timeRange) {
let addAnnotationScope = this.$scope.$new();
- let annotationTimeUnix = Math.round(event.pos.x);
- addAnnotationScope.annotationTimeUnix = annotationTimeUnix;
+ addAnnotationScope.annotationTimeRange = timeRange;
this.publishAppEvent('show-modal', {
src: 'public/app/features/dashboard/partials/addAnnotationModal.html',
From d553498a33299aeb62931cf553e36e1f1190e959 Mon Sep 17 00:00:00 2001
From: Alexander Zobnin
Date: Mon, 10 Apr 2017 20:22:58 +0300
Subject: [PATCH 008/365] graph(add annotation): initial backend implementation
#1286
---
pkg/api/annotations.go | 21 +++++++++++++++++++
pkg/api/api.go | 1 +
pkg/api/dtos/annotations.go | 8 +++++++
pkg/services/annotations/annotations.go | 8 +++++++
.../features/annotations/annotations_srv.ts | 13 ++++++++----
5 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go
index 48bf6c327ad..af72b9d3876 100644
--- a/pkg/api/annotations.go
+++ b/pkg/api/annotations.go
@@ -45,6 +45,27 @@ func GetAnnotations(c *middleware.Context) Response {
return Json(200, result)
}
+func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response {
+ repo := annotations.GetRepository()
+
+ item := annotations.Item{
+ OrgId: c.OrgId,
+ DashboardId: cmd.DashboardId,
+ PanelId: cmd.PanelId,
+ Epoch: cmd.Time / 1000,
+ Title: cmd.Title,
+ Text: cmd.Text,
+ }
+
+ err := repo.Save(&item)
+
+ if err != nil {
+ return ApiError(500, "Failed to save annotation", err)
+ }
+
+ return ApiSuccess("Annotation added")
+}
+
func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Response {
repo := annotations.GetRepository()
diff --git a/pkg/api/api.go b/pkg/api/api.go
index 843b68eb915..0b9a8acf851 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -277,6 +277,7 @@ func (hs *HttpServer) registerRoutes() {
}, reqEditorRole)
r.Get("/annotations", wrap(GetAnnotations))
+ r.Post("/annotations", bind(dtos.PostAnnotationsCmd{}), wrap(PostAnnotation))
r.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), wrap(DeleteAnnotations))
// error test
diff --git a/pkg/api/dtos/annotations.go b/pkg/api/dtos/annotations.go
index 45415978ee1..cd8c0c1d523 100644
--- a/pkg/api/dtos/annotations.go
+++ b/pkg/api/dtos/annotations.go
@@ -16,6 +16,14 @@ type Annotation struct {
Data *simplejson.Json `json:"data"`
}
+type PostAnnotationsCmd struct {
+ DashboardId int64 `json:"dashboardId"`
+ PanelId int64 `json:"panelId"`
+ Time int64 `json:"time"`
+ Title string `json:"title"`
+ Text string `json:"text"`
+}
+
type DeleteAnnotationsCmd struct {
AlertId int64 `json:"alertId"`
DashboardId int64 `json:"dashboardId"`
diff --git a/pkg/services/annotations/annotations.go b/pkg/services/annotations/annotations.go
index d9d15bca34b..a308f546c8a 100644
--- a/pkg/services/annotations/annotations.go
+++ b/pkg/services/annotations/annotations.go
@@ -21,6 +21,14 @@ type ItemQuery struct {
Limit int64 `json:"limit"`
}
+type PostParams struct {
+ DashboardId int64 `json:"dashboardId"`
+ PanelId int64 `json:"panelId"`
+ Epoch int64 `json:"epoch"`
+ Title string `json:"title"`
+ Text string `json:"text"`
+}
+
type DeleteParams struct {
AlertId int64 `json:"alertId"`
DashboardId int64 `json:"dashboardId"`
diff --git a/public/app/features/annotations/annotations_srv.ts b/public/app/features/annotations/annotations_srv.ts
index f50fce8e08a..d3b83982f51 100644
--- a/public/app/features/annotations/annotations_srv.ts
+++ b/public/app/features/annotations/annotations_srv.ts
@@ -126,13 +126,18 @@ export class AnnotationsSrv {
return this.globalAnnotationsPromise;
}
- postAnnotation(annotation) {
- console.log("POST /api/annotations\n", annotation);
+ postAnnotation(annotations) {
+ console.log("POST /api/annotations\n", annotations);
// Not implemented yet
- let implemented = false;
+ let implemented = true;
if (implemented) {
- return this.backendSrv.post('/api/annotations', annotation);
+ return Promise.all(_.map(annotations, annotation => {
+ return this.backendSrv.post('/api/annotations', annotation);
+ }))
+ .catch(error => {
+ console.log(error);
+ });
} else {
return Promise.resolve("Not implemented");
}
From 70bca219e3b39fa04df7efe7d0a524795b2faa45 Mon Sep 17 00:00:00 2001
From: Alexander Zobnin
Date: Tue, 11 Apr 2017 10:24:21 +0300
Subject: [PATCH 009/365] graph(add annotation): Add keybinding for CTRL key
---
public/app/plugins/panel/graph/graph.ts | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts
index 5aaf5324156..c9cd251777f 100755
--- a/public/app/plugins/panel/graph/graph.ts
+++ b/public/app/plugins/panel/graph/graph.ts
@@ -80,9 +80,11 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv) {
}, scope);
appEvents.on('graph-click', (event) => {
+ // Add event only for selected panel
+ let thisPanelEvent = event.panel.id === ctrl.panel.id;
// Select time for new annotation
- if (ctrl.inAddAnnotationMode) {
+ if (ctrl.inAddAnnotationMode && thisPanelEvent) {
let timeRange = {
from: event.pos.x,
to: null
@@ -93,6 +95,22 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv) {
}
}, scope);
+ // Add keybinding for Add Annotation mode
+ $(document).keydown(onCtrlKeyDown);
+ $(document).keyup(onCtrlKeyUp);
+
+ function onCtrlKeyDown(event) {
+ if (event.key === 'Control') {
+ ctrl.inAddAnnotationMode = true;
+ }
+ }
+
+ function onCtrlKeyUp(event) {
+ if (event.key === 'Control') {
+ ctrl.inAddAnnotationMode = false;
+ }
+ }
+
function getLegendHeight(panelHeight) {
if (!panel.legend.show || panel.legend.rightSide) {
return 0;
From 3ca3c9622696d5cce1b7d5c23f2056e97baae8fa Mon Sep 17 00:00:00 2001
From: Daniel Lee
Date: Thu, 6 Apr 2017 11:59:01 +0200
Subject: [PATCH 010/365] profiling: adds profiling and tracing
If grafana-server binary is started with the -profile flag then
tracing will create a trace.out file and pprof data can be accessed
on the 6060 port.
A custom port for pprof profiling can be set with the -profile-port
flag.
---
pkg/cmd/grafana-server/main.go | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go
index 7e8a6061a3f..cd3dd0dbd36 100644
--- a/pkg/cmd/grafana-server/main.go
+++ b/pkg/cmd/grafana-server/main.go
@@ -8,10 +8,14 @@ import (
"os/signal"
"path/filepath"
"runtime"
+ "runtime/trace"
"strconv"
"syscall"
"time"
+ "net/http"
+ _ "net/http/pprof"
+
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
@@ -44,12 +48,33 @@ func init() {
func main() {
v := flag.Bool("v", false, "prints current version and exits")
+ profile := flag.Bool("profile", false, "Turn on pprof profiling")
+ profilePort := flag.Int("profile-port", 6060, "Define custom port for profiling")
flag.Parse()
if *v {
fmt.Printf("Version %s (commit: %s)\n", version, commit)
os.Exit(0)
}
+ if *profile {
+ runtime.SetBlockProfileRate(1)
+ go func() {
+ http.ListenAndServe(fmt.Sprintf("localhost:%d", *profilePort), nil)
+ }()
+
+ f, err := os.Create("trace.out")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+
+ err = trace.Start(f)
+ if err != nil {
+ panic(err)
+ }
+ defer trace.Stop()
+ }
+
buildstampInt64, _ := strconv.ParseInt(buildstamp, 10, 64)
if buildstampInt64 == 0 {
buildstampInt64 = time.Now().Unix()
@@ -113,6 +138,8 @@ func listenToSystemSignals(server models.GrafanaServer) {
select {
case sig := <-signalChan:
+ // Stops trace if profiling has been enabled
+ trace.Stop()
server.Shutdown(0, fmt.Sprintf("system signal: %s", sig))
case code = <-exitChan:
server.Shutdown(code, "startup error")
From e0640487dfa5527b22d689b472a2a17ca23516a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 11 Apr 2017 13:38:13 +0200
Subject: [PATCH 011/365] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 024bc39d2dd..5c32dcdba40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,10 @@
* **InfluxDB**: Small fix for the "glow" when focus the field for LIMIT and SLIMIT [#7799](https://github.com/grafana/grafana/pull/7799) thx [@thuck](https://github.com/thuck)
* **Panels**: Delay loading & Lazy load panels as they become visible (scrolled into view) [#5216](https://github.com/grafana/grafana/issues/5216) thx [@jifwin](https://github.com/jifwin)
* **Graph**: Support auto grid min/max when using log scale [#3090](https://github.com/grafana/grafana/issues/3090), thx [@bigbenhur](https://github.com/bigbenhur)
+* **Graph**: Support for histograms [#600](https://github.com/grafana/grafana/issues/600)
* **Elasticsearch**: Support histogram aggregations [#3164](https://github.com/grafana/grafana/issues/3164)
+
## Minor Enchancements
* **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778)
From 9099119f6293f7648c43d074eac5d0e4e4a9266d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 11 Apr 2017 14:42:35 +0200
Subject: [PATCH 012/365] grunt: minor watch fix
---
tasks/options/watch.js | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/tasks/options/watch.js b/tasks/options/watch.js
index b74eb4a0c6a..545a149054a 100644
--- a/tasks/options/watch.js
+++ b/tasks/options/watch.js
@@ -54,19 +54,12 @@ module.exports = function(config, grunt) {
grunt.task.run('css');
}
- // if (/(\.ts)$/.test(filepath)) {
- // newPath = filepath.replace(/^public/, 'public_gen');
- // grunt.log.writeln('Copying to ' + newPath);
- // grunt.file.copy(filepath, newPath);
- //
- // // copy ts file also used by source maps
- // //changes changed file source to that of the changed file
- // grunt.config('typescript.build.src', filepath);
- // grunt.config('tslint.source.files.src', filepath);
- //
- // grunt.task.run('exec:tscompile');
- // grunt.task.run('exec:tslint');
- // }
+ if (/(\.ts)$/.test(filepath)) {
+ newPath = filepath.replace(/^public/, 'public_gen');
+ grunt.log.writeln('Copying to ' + newPath);
+ grunt.file.copy(filepath, newPath);
+ grunt.task.run('exec:tslint');
+ }
done();
firstRun = false;
From 89a7c2c686854790f5306ab6de7f7aa48a55643b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 11 Apr 2017 15:05:30 +0200
Subject: [PATCH 013/365] influxdb: validate database exist when saving data
source, fixes #7864
---
public/app/core/components/query_part/query_part.ts | 2 --
public/app/plugins/datasource/influxdb/datasource.ts | 11 ++++++++++-
public/app/plugins/datasource/influxdb/query_part.ts | 1 -
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/public/app/core/components/query_part/query_part.ts b/public/app/core/components/query_part/query_part.ts
index cf6780ac182..00538102fb2 100644
--- a/public/app/core/components/query_part/query_part.ts
+++ b/public/app/core/components/query_part/query_part.ts
@@ -119,5 +119,3 @@ export function identityRenderer(part, innerExpr) {
export function quotedIdentityRenderer(part, innerExpr) {
return '"' + part.params[0] + '"';
}
-
-
diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts
index dc6aaaf7a03..98c7ba87bd2 100644
--- a/public/app/plugins/datasource/influxdb/datasource.ts
+++ b/public/app/plugins/datasource/influxdb/datasource.ts
@@ -193,8 +193,17 @@ export default class InfluxDatasource {
}
testDatasource() {
- return this.metricFindQuery('SHOW MEASUREMENTS LIMIT 1').then(() => {
+ return this.metricFindQuery('SHOW DATABASES').then(res => {
+ let found = _.find(res, {text: this.database});
+ if (!found) {
+ return { status: "error", message: "Could not find the specified database name.", title: "DB Not found" };
+ }
return { status: "success", message: "Data source is working", title: "Success" };
+ }).catch(err => {
+ if (err.data && err.message) {
+ return { status: "error", message: err.data.message, title: "InfluxDB Error" };
+ }
+ return { status: "error", message: err.toString(), title: "InfluxDB Error" };
});
}
diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts
index 6b55b19f4d2..20274ccc580 100644
--- a/public/app/plugins/datasource/influxdb/query_part.ts
+++ b/public/app/plugins/datasource/influxdb/query_part.ts
@@ -292,7 +292,6 @@ register({
renderer: functionRenderer,
});
-debugger;
register({
type: 'holt_winters_with_fit',
addStrategy: addTransformationStrategy,
From a109049de4af907e38c2a31c9d1410ebd7331cf7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 11 Apr 2017 16:41:07 +0200
Subject: [PATCH 014/365] smpt: Added smtp docker block
---
docker/blocks/smtp/fig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docker/blocks/smtp/fig b/docker/blocks/smtp/fig
index c2d37e01c21..3aa25e01311 100644
--- a/docker/blocks/smtp/fig
+++ b/docker/blocks/smtp/fig
@@ -1,4 +1,4 @@
snmpd:
- build: blocks/snmpd
+ image: namshi/smtp
ports:
- - "161:161"
+ - "25:25"
From 07466b6725e2ea050eb2950bee3a473270fad6aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 11 Apr 2017 16:50:16 +0200
Subject: [PATCH 015/365] security: fixed returning info on weither user exists
or not in password reset call, fixes #7619
---
pkg/api/password.go | 3 ++-
public/app/partials/reset_password.html | 15 +++++++++++----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/pkg/api/password.go b/pkg/api/password.go
index f3c2b0b7058..e71f1317ee4 100644
--- a/pkg/api/password.go
+++ b/pkg/api/password.go
@@ -12,7 +12,8 @@ func SendResetPasswordEmail(c *middleware.Context, form dtos.SendResetPasswordEm
userQuery := m.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
if err := bus.Dispatch(&userQuery); err != nil {
- return ApiError(404, "User does not exist", err)
+ c.Logger.Info("Requested password reset for user that was not found", "user", userQuery.LoginOrEmail)
+ return ApiError(200, "Email sent", err)
}
emailCmd := m.SendResetPasswordEmailCommand{User: userQuery.Result}
diff --git a/public/app/partials/reset_password.html b/public/app/partials/reset_password.html
index b0807a06920..82024932a16 100644
--- a/public/app/partials/reset_password.html
+++ b/public/app/partials/reset_password.html
@@ -21,15 +21,22 @@
+
+
-
- An email with a reset link as been sent to the email address, you should receive it shortly.
-
+
+
+ An email with a reset link as been sent to the email address.
+ You should receive it shortly.
+
+
+
+