Commit 4d0982a2 by bergquist

Merge branch 'master' into alerting

parents 46ea3ed9 0ea3458f
......@@ -7,7 +7,12 @@
* **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.2 (unreleased)
* **Templating**: Fixed issue when combining row & panel repeats, fixes [#5790](https://github.com/grafana/grafana/issues/5790)
* **Drag&Drop**: Fixed issue with drag and drop in latest Chrome(51+), fixes [#5767](https://github.com/grafana/grafana/issues/5767)
* **Internal Metrics**: Fixed issue with dots in instance_name when sending internal metrics to Graphitge, fixes [#5739](https://github.com/grafana/grafana/issues/5739)
# 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)
......
......@@ -378,7 +378,7 @@ interval_seconds = 60
# Send internal Grafana metrics to graphite
; [metrics.graphite]
; address = localhost:2003
; prefix = service.grafana.%(instance_name)s
; prefix = service.grafana.%(instance_name)s.
[grafana_net]
url = https://grafana.net
......
......@@ -306,7 +306,7 @@ enabled = true
# Send internal metrics to Graphite
; [metrics.graphite]
; address = localhost:2003
; prefix = service.grafana.%(instance_name)s
; prefix = service.grafana.%(instance_name)s.
#################################### Internal Grafana Metrics ##########################
# Url used to to import dashboards directly from Grafana.net
......
......@@ -32,7 +32,7 @@ will be expected to export different things. You can find what's expected for [d
and [apps](./apps.md) plugins in the documentation.
## Start developing your plugin
There are two ways that you can start developing a Grafana plugin.
There are three ways that you can start developing a Grafana plugin.
1. Setup a Grafana development environment. [(described here)](http://docs.grafana.org/project/building_from_source/) and place your plugin in the ```data/plugins``` folder.
2. Install Grafana and place your plugin in the plugins directory which is set in your [config file](../installation/configuration.md). By default this is `/var/lib/grafana/plugins` on Linux systems.
......
......@@ -28,17 +28,18 @@ var customMetricsDimensionsMap map[string]map[string]map[string]*CustomMetricsCa
func init() {
metricsMap = map[string][]string{
"AWS/AutoScaling": {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"},
"AWS/Billing": {"EstimatedCharges"},
"AWS/CloudFront": {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},
"AWS/CloudSearch": {"SuccessfulRequests", "SearchableDocuments", "IndexUtilization", "Partitions"},
"AWS/DynamoDB": {"ConditionalCheckFailedRequests", "ConsumedReadCapacityUnits", "ConsumedWriteCapacityUnits", "OnlineIndexConsumedWriteCapacity", "OnlineIndexPercentageProgress", "OnlineIndexThrottleEvents", "ProvisionedReadCapacityUnits", "ProvisionedWriteCapacityUnits", "ReadThrottleEvents", "ReturnedBytes", "ReturnedItemCount", "ReturnedRecordsCount", "SuccessfulRequestLatency", "SystemErrors", "ThrottledRequests", "UserErrors", "WriteThrottleEvents"},
"AWS/EBS": {"VolumeReadBytes", "VolumeWriteBytes", "VolumeReadOps", "VolumeWriteOps", "VolumeTotalReadTime", "VolumeTotalWriteTime", "VolumeIdleTime", "VolumeQueueLength", "VolumeThroughputPercentage", "VolumeConsumedReadWriteOps", "BurstBalance"},
"AWS/EC2": {"CPUCreditUsage", "CPUCreditBalance", "CPUUtilization", "DiskReadOps", "DiskWriteOps", "DiskReadBytes", "DiskWriteBytes", "NetworkIn", "NetworkOut", "NetworkPacketsIn", "NetworkPacketsOut", "StatusCheckFailed", "StatusCheckFailed_Instance", "StatusCheckFailed_System"},
"AWS/EC2Spot": {"AvailableInstancePoolsCount", "BidsSubmittedForCapacity", "EligibleInstancePoolCount", "FulfilledCapacity", "MaxPercentCapacityAllocation", "PendingCapacity", "PercentCapacityAllocation", "TargetCapacity", "TerminatingCapacity"},
"AWS/ECS": {"CPUReservation", "MemoryReservation", "CPUUtilization", "MemoryUtilization"},
"AWS/EFS": {"BurstCreditBalance", "ClientConnections", "DataReadIOBytes", "DataWriteIOBytes", "MetadataIOBytes", "TotalIOBytes", "PermittedThroughput", "PercentIOLimit"},
"AWS/ELB": {"HealthyHostCount", "UnHealthyHostCount", "RequestCount", "Latency", "HTTPCode_ELB_4XX", "HTTPCode_ELB_5XX", "HTTPCode_Backend_2XX", "HTTPCode_Backend_3XX", "HTTPCode_Backend_4XX", "HTTPCode_Backend_5XX", "BackendConnectionErrors", "SurgeQueueLength", "SpilloverCount"},
"AWS/ApplicationELB": {"ActiveConnectionCount", "ClientTLSNegotiationErrorCount", "HealthyHostCount", "HTTPCode_ELB_4XX_Count", "HTTPCode_ELB_5XX_Count", "HTTPCode_Target_2XX_Count", "HTTPCode_Target_3XX_Count", "HTTPCode_Target_4XX_Count", "HTTPCode_Target_5XX_Count", "NewConnectionCount", "ProcessedBytes", "RejectedConnectionCount", "RequestCount", "TargetConnectionErrorCount", "TargetResponseTime", "TargetTLSNegotiationErrorCount", "UnhealthyHostCount"},
"AWS/AutoScaling": {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"},
"AWS/Billing": {"EstimatedCharges"},
"AWS/CloudFront": {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},
"AWS/CloudSearch": {"SuccessfulRequests", "SearchableDocuments", "IndexUtilization", "Partitions"},
"AWS/DynamoDB": {"ConditionalCheckFailedRequests", "ConsumedReadCapacityUnits", "ConsumedWriteCapacityUnits", "OnlineIndexConsumedWriteCapacity", "OnlineIndexPercentageProgress", "OnlineIndexThrottleEvents", "ProvisionedReadCapacityUnits", "ProvisionedWriteCapacityUnits", "ReadThrottleEvents", "ReturnedBytes", "ReturnedItemCount", "ReturnedRecordsCount", "SuccessfulRequestLatency", "SystemErrors", "ThrottledRequests", "UserErrors", "WriteThrottleEvents"},
"AWS/EBS": {"VolumeReadBytes", "VolumeWriteBytes", "VolumeReadOps", "VolumeWriteOps", "VolumeTotalReadTime", "VolumeTotalWriteTime", "VolumeIdleTime", "VolumeQueueLength", "VolumeThroughputPercentage", "VolumeConsumedReadWriteOps", "BurstBalance"},
"AWS/EC2": {"CPUCreditUsage", "CPUCreditBalance", "CPUUtilization", "DiskReadOps", "DiskWriteOps", "DiskReadBytes", "DiskWriteBytes", "NetworkIn", "NetworkOut", "NetworkPacketsIn", "NetworkPacketsOut", "StatusCheckFailed", "StatusCheckFailed_Instance", "StatusCheckFailed_System"},
"AWS/EC2Spot": {"AvailableInstancePoolsCount", "BidsSubmittedForCapacity", "EligibleInstancePoolCount", "FulfilledCapacity", "MaxPercentCapacityAllocation", "PendingCapacity", "PercentCapacityAllocation", "TargetCapacity", "TerminatingCapacity"},
"AWS/ECS": {"CPUReservation", "MemoryReservation", "CPUUtilization", "MemoryUtilization"},
"AWS/EFS": {"BurstCreditBalance", "ClientConnections", "DataReadIOBytes", "DataWriteIOBytes", "MetadataIOBytes", "TotalIOBytes", "PermittedThroughput", "PercentIOLimit"},
"AWS/ELB": {"HealthyHostCount", "UnHealthyHostCount", "RequestCount", "Latency", "HTTPCode_ELB_4XX", "HTTPCode_ELB_5XX", "HTTPCode_Backend_2XX", "HTTPCode_Backend_3XX", "HTTPCode_Backend_4XX", "HTTPCode_Backend_5XX", "BackendConnectionErrors", "SurgeQueueLength", "SpilloverCount"},
"AWS/ElastiCache": {
"CPUUtilization", "FreeableMemory", "NetworkBytesIn", "NetworkBytesOut", "SwapUsage",
"BytesUsedForCacheItems", "BytesReadIntoMemcached", "BytesWrittenOutFromMemcached", "CasBadval", "CasHits", "CasMisses", "CmdFlush", "CmdGet", "CmdSet", "CurrConnections", "CurrItems", "DecrHits", "DecrMisses", "DeleteHits", "DeleteMisses", "Evictions", "GetHits", "GetMisses", "IncrHits", "IncrMisses", "Reclaimed",
......@@ -86,6 +87,7 @@ func init() {
"AWS/WorkSpaces": {"Available", "Unhealthy", "ConnectionAttempt", "ConnectionSuccess", "ConnectionFailure", "SessionLaunchTime", "InSessionLatency", "SessionDisconnect"},
}
dimensionsMap = map[string][]string{
"AWS/ApplicationELB": {"LoadBalancer", "TargetGroup", "AvailabilityZone"},
"AWS/AutoScaling": {"AutoScalingGroupName"},
"AWS/Billing": {"ServiceName", "LinkedAccount", "Currency"},
"AWS/CloudFront": {"DistributionId", "Region"},
......
......@@ -33,7 +33,7 @@ func CreateGraphitePublisher() (*GraphitePublisher, error) {
prefix := graphiteSection.Key("prefix").Value()
if prefix == "" {
prefix = "service.grafana.%(instance_name)s"
prefix = "service.grafana.%(instance_name)s."
}
publisher.prefix = strings.Replace(prefix, "%(instance_name)s", safeInstanceName, -1)
......
......@@ -20,7 +20,7 @@ func TestGraphitePublisher(t *testing.T) {
sec, err := setting.Cfg.NewSection("metrics.graphite")
sec.NewKey("prefix", "service.grafana.%(instance_name)s.")
sec.NewKey("address", "localhost:2003")
sec.NewKey("address", "localhost:2001")
So(err, ShouldBeNil)
......@@ -31,6 +31,7 @@ func TestGraphitePublisher(t *testing.T) {
So(publisher, ShouldNotBeNil)
So(publisher.prefix, ShouldEqual, "service.grafana.hostname_with_dots_com.")
So(publisher.address, ShouldEqual, "localhost:2001")
})
Convey("Test graphite publisher default values", t, func() {
......
......@@ -27,10 +27,19 @@ export class DynamicDashboardSrv {
this.iteration = (this.iteration || new Date().getTime()) + 1;
var cleanUpOnly = options.cleanUpOnly;
var i, j, row, panel;
// cleanup scopedVars
for (i = 0; i < this.dashboard.rows.length; i++) {
row = this.dashboard.rows[i];
for (j = 0; j < row.panels.length; j++) {
delete row.panels[j].scopedVars;
}
}
for (i = 0; i < this.dashboard.rows.length; i++) {
row = this.dashboard.rows[i];
// handle row repeats
if (row.repeat) {
if (!cleanUpOnly) {
......@@ -54,8 +63,6 @@ export class DynamicDashboardSrv {
// clean up old left overs
row.panels = _.without(row.panels, panel);
j = j - 1;
} else if (!_.isEmpty(panel.scopedVars) && panel.repeatIteration !== this.iteration) {
panel.scopedVars = {};
}
}
}
......@@ -120,7 +127,6 @@ export class DynamicDashboardSrv {
panel = copy.panels[i];
panel.scopedVars = {};
panel.scopedVars[variable.name] = option;
panel.repeatIteration = this.iteration;
}
});
}
......
......@@ -93,10 +93,29 @@ dynamicDashScenario('given dashboard with panel repeat', function(ctx) {
});
});
describe('After a second iteration with different variable', function() {
beforeEach(function() {
ctx.dash.templating.list.push({
name: 'server',
current: { text: 'se1, se2, se3', value: ['se1']},
options: [{text: 'se1', value: 'se1', selected: true}]
});
ctx.rows[0].panels[0].repeat = "server";
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should remove scopedVars value for last variable', function() {
expect(ctx.rows[0].panels[0].scopedVars.apps).to.be(undefined);
});
it('should have new variable value in scopedVars', function() {
expect(ctx.rows[0].panels[0].scopedVars.server.value).to.be("se1");
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
ctx.dash.templating.list[0].options[1].selected = false;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
......@@ -116,7 +135,7 @@ dynamicDashScenario('given dashboard with panel repeat', function(ctx) {
});
it('should remove scoped vars from reused panel', function() {
expect(ctx.rows[0].panels[0].scopedVars).to.be.empty();
expect(ctx.rows[0].panels[0].scopedVars).to.be(undefined);
});
});
......@@ -165,6 +184,7 @@ dynamicDashScenario('given dashboard with row repeat', function(ctx) {
it('should generate a repeartRowId based on repeat row index', function() {
expect(ctx.rows[1].repeatRowId).to.be(1);
expect(ctx.rows[1].repeatIteration).to.be(ctx.dynamicDashboardSrv.iteration);
});
it('should set scopedVars on row panels', function() {
......
......@@ -47,7 +47,8 @@ describe('given dashboard with repeated panels', function() {
});
dash.rows.push({
repeat: null,
repeatRowId: 1
repeatRowId: 1,
panels: [],
});
var datasourceSrvStub = {
......
......@@ -83,7 +83,6 @@ function ($, _) {
// 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.
hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex);
hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]);
}
results.push({
......
define([
'app/features/dashboard/dynamic_dashboard_srv',
'app/features/dashboard/dashboardSrv'
], function() {
'use strict';
function dynamicDashScenario(desc, func) {
describe(desc, function() {
var ctx = {};
ctx.setup = function (setupFunc) {
beforeEach(module('grafana.services'));
beforeEach(module('grafana.core'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', {
user: { timezone: 'utc'}
});
}));
beforeEach(inject(function(dynamicDashboardSrv, dashboardSrv) {
ctx.dynamicDashboardSrv = dynamicDashboardSrv;
ctx.dashboardSrv = dashboardSrv;
var model = {
rows: [],
templating: { list: [] }
};
setupFunc(model);
ctx.dash = ctx.dashboardSrv.create(model);
ctx.dynamicDashboardSrv.init(ctx.dash);
ctx.rows = ctx.dash.rows;
}));
};
func(ctx);
});
}
dynamicDashScenario('given dashboard with panel repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
panels: [{id: 2, repeat: 'apps'}]
});
dash.templating.list.push({
name: 'apps',
current: {
text: 'se1, se2, se3',
value: ['se1', 'se2', 'se3']
},
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
{text: 'se3', value: 'se3', selected: true},
{text: 'se4', value: 'se4', selected: false}
]
});
});
it('should repeat panel one time', function() {
expect(ctx.rows[0].panels.length).to.be(3);
});
it('should mark panel repeated', function() {
expect(ctx.rows[0].panels[0].repeat).to.be('apps');
expect(ctx.rows[0].panels[1].repeatPanelId).to.be(2);
});
it('should set scopedVars on panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.apps.value).to.be('se1');
expect(ctx.rows[0].panels[1].scopedVars.apps.value).to.be('se2');
expect(ctx.rows[0].panels[2].scopedVars.apps.value).to.be('se3');
});
describe('After a second iteration', function() {
var repeatedPanelAfterIteration1;
beforeEach(function() {
repeatedPanelAfterIteration1 = ctx.rows[0].panels[1];
ctx.rows[0].panels[0].fill = 10;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should have reused same panel instances', function() {
expect(ctx.rows[0].panels[1]).to.be(repeatedPanelAfterIteration1);
});
it('reused panel should copy properties from source', function() {
expect(ctx.rows[0].panels[1].fill).to.be(10);
});
it('should have same panel count', function() {
expect(ctx.rows[0].panels.length).to.be(3);
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
ctx.dash.templating.list[0].options[1].selected = false;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should clean up repeated panel', function() {
expect(ctx.rows[0].panels.length).to.be(2);
});
});
describe('After a second iteration and panel repeat is turned off', function() {
beforeEach(function() {
ctx.rows[0].panels[0].repeat = null;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should clean up repeated panel', function() {
expect(ctx.rows[0].panels.length).to.be(1);
});
it('should remove scoped vars from reused panel', function() {
expect(ctx.rows[0].panels[0].scopedVars).to.be.empty();
});
});
});
dynamicDashScenario('given dashboard with row repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
repeat: 'servers',
panels: [{id: 2}]
});
dash.rows.push({panels: []});
dash.templating.list.push({
name: 'servers',
current: {
text: 'se1, se2',
value: ['se1', 'se2']
},
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
]
});
});
it('should repeat row one time', function() {
expect(ctx.rows.length).to.be(3);
});
it('should keep panel ids on first row', function() {
expect(ctx.rows[0].panels[0].id).to.be(2);
});
it('should keep first row as repeat', function() {
expect(ctx.rows[0].repeat).to.be('servers');
});
it('should clear repeat field on repeated row', function() {
expect(ctx.rows[1].repeat).to.be(null);
});
it('should add scopedVars to rows', function() {
expect(ctx.rows[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].scopedVars.servers.value).to.be('se2');
});
it('should generate a repeartRowId based on repeat row index', function() {
expect(ctx.rows[1].repeatRowId).to.be(1);
expect(ctx.rows[1].repeatIteration).to.be(ctx.dynamicDashboardSrv.iteration);
});
it('should set scopedVars on row panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].panels[0].scopedVars.servers.value).to.be('se2');
});
describe('After a second iteration', function() {
var repeatedRowAfterFirstIteration;
beforeEach(function() {
repeatedRowAfterFirstIteration = ctx.rows[1];
ctx.rows[0].height = 500;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should still only have 2 rows', function() {
expect(ctx.rows.length).to.be(3);
});
it.skip('should have updated props from source', function() {
expect(ctx.rows[1].height).to.be(500);
});
it('should reuse row instance', function() {
expect(ctx.rows[1]).to.be(repeatedRowAfterFirstIteration);
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
ctx.dash.templating.list[0].options[1].selected = false;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should remove repeated second row', function() {
expect(ctx.rows.length).to.be(2);
});
});
});
dynamicDashScenario('given dashboard with row repeat and panel repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
repeat: 'servers',
panels: [{id: 2, repeat: 'metric'}]
});
dash.templating.list.push({
name: 'servers',
current: { text: 'se1, se2', value: ['se1', 'se2'] },
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
]
});
dash.templating.list.push({
name: 'metric',
current: { text: 'm1, m2', value: ['m1', 'm2'] },
options: [
{text: 'm1', value: 'm1', selected: true},
{text: 'm2', value: 'm2', selected: true},
]
});
});
it('should repeat row one time', function() {
expect(ctx.rows.length).to.be(2);
});
it('should repeat panel on both rows', function() {
expect(ctx.rows[0].panels.length).to.be(2);
expect(ctx.rows[1].panels.length).to.be(2);
});
it('should keep panel ids on first row', function() {
expect(ctx.rows[0].panels[0].id).to.be(2);
});
it('should mark second row as repeated', function() {
expect(ctx.rows[0].repeat).to.be('servers');
});
it('should clear repeat field on repeated row', function() {
expect(ctx.rows[1].repeat).to.be(null);
});
it('should generate a repeartRowId based on repeat row index', function() {
expect(ctx.rows[1].repeatRowId).to.be(1);
});
it('should set scopedVars on row panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].panels[0].scopedVars.servers.value).to.be('se2');
});
});
});
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